diff options
Diffstat (limited to 'include/cru/common')
-rw-r--r-- | include/cru/common/Base.hpp | 13 | ||||
-rw-r--r-- | include/cru/common/ClonablePtr.hpp | 204 | ||||
-rw-r--r-- | include/cru/common/Event.hpp | 133 | ||||
-rw-r--r-- | include/cru/common/Format.hpp | 23 | ||||
-rw-r--r-- | include/cru/common/HandlerRegistry.hpp | 86 | ||||
-rw-r--r-- | include/cru/common/Logger.hpp | 2 | ||||
-rw-r--r-- | include/cru/common/SelfResolvable.hpp | 24 | ||||
-rw-r--r-- | include/cru/common/StringUtil.hpp | 21 |
8 files changed, 467 insertions, 39 deletions
diff --git a/include/cru/common/Base.hpp b/include/cru/common/Base.hpp index a5a9421d..560f83bb 100644 --- a/include/cru/common/Base.hpp +++ b/include/cru/common/Base.hpp @@ -1,8 +1,8 @@ #pragma once #include "PreConfig.hpp" +#include <exception> #include <gsl/gsl> - #include <stdexcept> #define CRU_UNUSED(entity) static_cast<void>(entity); @@ -42,12 +42,17 @@ struct Interface { virtual ~Interface() = default; }; -[[noreturn]] inline void UnreachableCode() { - throw std::runtime_error("Unreachable code."); -} +[[noreturn]] inline void UnreachableCode() { std::terminate(); } using Index = gsl::index; +// https://www.boost.org/doc/libs/1_54_0/doc/html/hash/reference.html#boost.hash_combine +template <class T> +inline void hash_combine(std::size_t& s, const T& v) { + std::hash<T> h; + s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); +} + #define CRU_DEFINE_CLASS_LOG_TAG(tag) \ private: \ constexpr static std::u16string_view log_tag = tag; diff --git a/include/cru/common/ClonablePtr.hpp b/include/cru/common/ClonablePtr.hpp new file mode 100644 index 00000000..5e4b80c9 --- /dev/null +++ b/include/cru/common/ClonablePtr.hpp @@ -0,0 +1,204 @@ +#pragma once + +#include <cstddef> +#include <functional> +#include <memory> +#include <type_traits> + +namespace cru { +template <typename TClonable> +class ClonablePtr { + template <typename T> + friend class ClonablePtr; + + public: + using element_type = typename std::unique_ptr<TClonable>::element_type; + using pointer = typename std::unique_ptr<TClonable>::pointer; + + ClonablePtr() = default; + ClonablePtr(std::nullptr_t) noexcept : ptr_(nullptr) {} + explicit ClonablePtr(pointer p) noexcept : ptr_(p) {} + ClonablePtr(std::unique_ptr<element_type>&& p) noexcept + : ptr_(std::move(p)) {} + template <typename O, + std::enable_if_t<std::is_convertible_v< + typename ClonablePtr<O>::pointer, pointer>, + int> = 0> + ClonablePtr(std::unique_ptr<O>&& p) : ptr_(std::move(p)) {} + ClonablePtr(const ClonablePtr& other) : ptr_(other.ptr_->Clone()) {} + ClonablePtr(ClonablePtr&& other) = default; + template <typename O, + std::enable_if_t<std::is_convertible_v< + typename ClonablePtr<O>::pointer, pointer>, + int> = 0> + ClonablePtr(const ClonablePtr<O>& other) : ptr_(other.ptr_->Clone()) {} + template <typename O, + std::enable_if_t<std::is_convertible_v< + typename ClonablePtr<O>::pointer, pointer>, + int> = 0> + ClonablePtr(ClonablePtr<O>&& other) noexcept : ptr_(std::move(other.ptr_)) {} + ClonablePtr& operator=(std::nullptr_t) noexcept { + ptr_ = nullptr; + return *this; + } + ClonablePtr& operator=(std::unique_ptr<element_type>&& other) noexcept { + ptr_ = std::move(other); + return *this; + } + template <typename O, + std::enable_if_t<std::is_convertible_v< + typename ClonablePtr<O>::pointer, pointer>, + int> = 0> + ClonablePtr& operator=(std::unique_ptr<O>&& p) noexcept { + ptr_ = std::move(p); + return *this; + } + ClonablePtr& operator=(const ClonablePtr& other) { + if (this != &other) { + ptr_ = std::unique_ptr<element_type>(other.ptr_->Clone()); + } + return *this; + } + ClonablePtr& operator=(ClonablePtr&& other) = default; + template <typename O, + std::enable_if_t<std::is_convertible_v< + typename ClonablePtr<O>::pointer, pointer>, + int> = 0> + ClonablePtr& operator=(const ClonablePtr<O>& other) noexcept { + if (this != &other) { + ptr_ = std::unique_ptr<element_type>(other.ptr_->Clone()); + } + return *this; + } + template <typename O, + std::enable_if_t<std::is_convertible_v< + typename ClonablePtr<O>::pointer, pointer>, + int> = 0> + ClonablePtr& operator=(ClonablePtr<O>&& other) noexcept { + ptr_ = std::move(other.ptr_); + } + + ~ClonablePtr() = default; + + public: + pointer release() noexcept { return ptr_.release(); } + void reset(pointer p = pointer()) noexcept { ptr_.reset(p); } + void swap(ClonablePtr& other) noexcept { ptr_.swap(other.ptr_); } + + public: + pointer get() const noexcept { return ptr_.get(); } + + operator bool() const noexcept { return ptr_; } + + element_type& operator*() const noexcept { return *ptr_; } + pointer operator->() const noexcept { return ptr_.get(); } + + private: + std::unique_ptr<element_type> ptr_; +}; + +template <typename T> +void swap(ClonablePtr<T>& left, ClonablePtr<T>& right) noexcept { + left.swap(right); +} + +template <typename T> +bool operator==(const ClonablePtr<T>& left, const ClonablePtr<T>& right) { + return left.get() == right.get(); +} + +template <typename T> +bool operator!=(const ClonablePtr<T>& left, const ClonablePtr<T>& right) { + return left.get() != right.get(); +} + +template <typename T> +bool operator<(const ClonablePtr<T>& left, const ClonablePtr<T>& right) { + return left.get() < right.get(); +} + +template <typename T> +bool operator<=(const ClonablePtr<T>& left, const ClonablePtr<T>& right) { + return left.get() <= right.get(); +} + +template <typename T> +bool operator>(const ClonablePtr<T>& left, const ClonablePtr<T>& right) { + return left.get() > right.get(); +} + +template <typename T> +bool operator>=(const ClonablePtr<T>& left, const ClonablePtr<T>& right) { + return left.get() >= right.get(); +} + +template <typename T> +bool operator==(const ClonablePtr<T>& left, std::nullptr_t) { + return left.get() == nullptr; +} + +template <typename T> +bool operator!=(const ClonablePtr<T>& left, std::nullptr_t) { + return left.get() != nullptr; +} + +template <typename T> +bool operator<(const ClonablePtr<T>& left, std::nullptr_t) { + return left.get() < nullptr; +} + +template <typename T> +bool operator<=(const ClonablePtr<T>& left, std::nullptr_t) { + return left.get() <= nullptr; +} + +template <typename T> +bool operator>(const ClonablePtr<T>& left, std::nullptr_t) { + return left.get() > nullptr; +} + +template <typename T> +bool operator>=(const ClonablePtr<T>& left, std::nullptr_t) { + return left.get() >= nullptr; +} + +template <typename T> +bool operator==(std::nullptr_t, const ClonablePtr<T>& right) { + return nullptr == right.get(); +} + +template <typename T> +bool operator!=(std::nullptr_t, const ClonablePtr<T>& right) { + return nullptr != right.get(); +} + +template <typename T> +bool operator<(std::nullptr_t, const ClonablePtr<T>& right) { + return nullptr < right.get(); +} + +template <typename T> +bool operator<=(std::nullptr_t, const ClonablePtr<T>& right) { + return nullptr <= right.get(); +} + +template <typename T> +bool operator>(std::nullptr_t, const ClonablePtr<T>& right) { + return nullptr > right.get(); +} + +template <typename T> +bool operator>=(std::nullptr_t, const ClonablePtr<T>& right) { + return nullptr >= right.get(); +} + +} // namespace cru + +namespace std { +template <typename T> +struct hash<cru::ClonablePtr<T>> { + std::size_t operator()(const cru::ClonablePtr<T>& p) const { + return std::hash<typename cru::ClonablePtr<T>::pointer>(p.get()); + } +}; +} // namespace std diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp index 377ca7f3..b6999aa4 100644 --- a/include/cru/common/Event.hpp +++ b/include/cru/common/Event.hpp @@ -5,14 +5,19 @@ #include <algorithm> #include <functional> +#include <initializer_list> #include <memory> #include <utility> +#include <variant> #include <vector> namespace cru { class EventRevoker; namespace details { +template <class> +inline constexpr bool always_false_v = false; + // Base class of all Event<T...>. // It erases event args types and provides a // unified form to create event revoker and @@ -24,10 +29,8 @@ class EventBase : public SelfResolvable<EventBase> { using EventHandlerToken = long; EventBase() {} - EventBase(const EventBase& other) = delete; - EventBase(EventBase&& other) = delete; - EventBase& operator=(const EventBase& other) = delete; - EventBase& operator=(EventBase&& other) = delete; + CRU_DELETE_COPY(EventBase) + CRU_DEFAULT_MOVE(EventBase) virtual ~EventBase() = default; // Remove the handler with the given token. If the token @@ -84,78 +87,114 @@ using DeducedEventArgs = std::conditional_t< std::is_lvalue_reference_v<TRaw>, TRaw, std::conditional_t<std::is_scalar_v<TRaw>, TRaw, const TRaw&>>; +struct IBaseEvent { + protected: + IBaseEvent() = default; + CRU_DELETE_COPY(IBaseEvent) + CRU_DEFAULT_MOVE(IBaseEvent) + ~IBaseEvent() = default; // Note that user can't destroy a Event via IEvent. + // So destructor should be protected. + + using SpyOnlyHandler = std::function<void()>; + + public: + virtual EventRevoker AddSpyOnlyHandler(SpyOnlyHandler handler) = 0; +}; + // Provides an interface of event. // IEvent only allow to add handler but not to raise the event. You may // want to create an Event object and expose IEvent only so users won't // be able to emit the event. template <typename TEventArgs> -struct IEvent { +struct IEvent : virtual IBaseEvent { public: using EventArgs = DeducedEventArgs<TEventArgs>; using EventHandler = std::function<void(EventArgs)>; + using ShortCircuitHandler = std::function<bool(EventArgs)>; protected: IEvent() = default; - IEvent(const IEvent& other) = delete; - IEvent(IEvent&& other) = delete; - IEvent& operator=(const IEvent& other) = delete; - IEvent& operator=(IEvent&& other) = delete; + CRU_DELETE_COPY(IEvent) + CRU_DEFAULT_MOVE(IEvent) ~IEvent() = default; // Note that user can't destroy a Event via IEvent. So // destructor should be protected. public: - virtual EventRevoker AddHandler(const EventHandler& handler) = 0; - virtual EventRevoker AddHandler(EventHandler&& handler) = 0; + virtual EventRevoker AddHandler(EventHandler handler) = 0; + virtual EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) = 0; + virtual EventRevoker PrependShortCircuitHandler( + ShortCircuitHandler handler) = 0; }; // A non-copyable non-movable Event class. // It stores a list of event handlers. template <typename TEventArgs> class Event : public details::EventBase, public IEvent<TEventArgs> { + using typename IEvent<TEventArgs>::EventArgs; + + using typename IBaseEvent::SpyOnlyHandler; using typename IEvent<TEventArgs>::EventHandler; + using typename IEvent<TEventArgs>::ShortCircuitHandler; private: struct HandlerData { - HandlerData(EventHandlerToken token, EventHandler handler) - : token(token), handler(handler) {} + HandlerData(EventHandlerToken token, ShortCircuitHandler handler) + : token(token), handler(std::move(handler)) {} EventHandlerToken token; - EventHandler handler; + ShortCircuitHandler handler; }; public: Event() = default; - Event(const Event&) = delete; - Event& operator=(const Event&) = delete; - Event(Event&&) = delete; - Event& operator=(Event&&) = delete; + CRU_DELETE_COPY(Event) + CRU_DEFAULT_MOVE(Event) ~Event() = default; - EventRevoker AddHandler(const EventHandler& handler) override { + EventRevoker AddSpyOnlyHandler(SpyOnlyHandler handler) override { + return AddShortCircuitHandler([handler = std::move(handler)](EventArgs) { + handler(); + return false; + }); + } + + EventRevoker AddHandler(EventHandler handler) override { + return AddShortCircuitHandler( + [handler = std::move(handler)](EventArgs args) { + handler(args); + return false; + }); + } + + // Handler return true to short circuit following handlers. + EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) override { const auto token = current_token_++; - this->handler_data_list_.emplace_back(token, handler); + this->handler_data_list_.emplace_back(token, std::move(handler)); return CreateRevoker(token); } - EventRevoker AddHandler(EventHandler&& handler) override { + // Handler return true to short circuit following handlers. + EventRevoker PrependShortCircuitHandler( + ShortCircuitHandler handler) override { const auto token = current_token_++; - this->handler_data_list_.emplace_back(token, std::move(handler)); + this->handler_data_list_.emplace(this->handler_data_list_.cbegin(), token, + std::move(handler)); return CreateRevoker(token); } // This method will make a copy of all handlers. Because user might delete a - // handler in a handler, which may lead to seg fault as the handler is deleted - // while being executed. - // Thanks to this behavior, all handlers will be taken a snapshot when Raise - // is called, so even if you delete a handler during this period, all handlers - // in the snapshot will be executed. - void Raise(typename IEvent<TEventArgs>::EventArgs args) { - std::vector<EventHandler> handlers; + // handler in a handler, which may lead to seg fault as the handler is + // deleted while being executed. Thanks to this behavior, all handlers will + // be taken a snapshot when Raise is called, so even if you delete a handler + // during this period, all handlers in the snapshot will be executed. + void Raise(EventArgs args) { + std::vector<ShortCircuitHandler> handlers; handlers.reserve(this->handler_data_list_.size()); for (const auto& data : this->handler_data_list_) { handlers.push_back(data.handler); } for (const auto& handler : handlers) { - handler(args); + auto short_circuit = handler(args); + if (short_circuit) return; } } @@ -183,6 +222,7 @@ struct EventRevokerDestroyer { }; } // namespace details +// A guard class for event revoker. Automatically revoke it when destroyed. class EventRevokerGuard { public: EventRevokerGuard() = default; @@ -201,7 +241,9 @@ class EventRevokerGuard { return *revoker_; } - void Release() { revoker_.release(); } + EventRevoker Release() { return std::move(*revoker_.release()); } + + void Reset() { revoker_.reset(); } void Reset(EventRevoker&& revoker) { revoker_.reset(new EventRevoker(std::move(revoker))); @@ -209,5 +251,32 @@ class EventRevokerGuard { private: std::unique_ptr<EventRevoker, details::EventRevokerDestroyer> revoker_; -}; // namespace cru +}; + +class EventRevokerListGuard { + public: + EventRevokerListGuard() = default; + EventRevokerListGuard(const EventRevokerListGuard& other) = delete; + EventRevokerListGuard(EventRevokerListGuard&& other) = default; + EventRevokerListGuard& operator=(const EventRevokerListGuard& other) = delete; + EventRevokerListGuard& operator=(EventRevokerListGuard&& other) = default; + ~EventRevokerListGuard() = default; + + public: + void Add(EventRevoker&& revoker) { + event_revoker_guard_list_.push_back(EventRevokerGuard(std::move(revoker))); + } + + EventRevokerListGuard& operator+=(EventRevoker&& revoker) { + this->Add(std::move(revoker)); + return *this; + } + + void Clear() { event_revoker_guard_list_.clear(); } + + bool IsEmpty() const { return event_revoker_guard_list_.empty(); } + + private: + std::vector<EventRevokerGuard> event_revoker_guard_list_; +}; } // namespace cru diff --git a/include/cru/common/Format.hpp b/include/cru/common/Format.hpp new file mode 100644 index 00000000..59f34036 --- /dev/null +++ b/include/cru/common/Format.hpp @@ -0,0 +1,23 @@ +#pragma once +#include "Base.hpp" + +#include "StringUtil.hpp" + +#include <array> +#include <charconv> +#include <string> +#include <string_view> +#include <system_error> +#include <type_traits> + +namespace cru { +template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>> +std::u16string ToUtf16String(T number) { + std::array<char, 40> buffer; + auto result = + std::to_chars(buffer.data(), buffer.data() + buffer.size(), number); + Ensures(result.ec == std::errc()); + std::string_view utf8_result(buffer.data(), result.ptr - buffer.data()); + return ToUtf16(utf8_result); +} +} // namespace cru diff --git a/include/cru/common/HandlerRegistry.hpp b/include/cru/common/HandlerRegistry.hpp new file mode 100644 index 00000000..bd74a9e0 --- /dev/null +++ b/include/cru/common/HandlerRegistry.hpp @@ -0,0 +1,86 @@ +#pragma once +#include "Base.hpp" + +#include <algorithm> +#include <functional> +#include <utility> +#include <vector> + +namespace cru { + +template <typename T> +class HandlerRegistryIterator { + public: + using RawIterator = + typename std::vector<std::pair<int, std::function<T>>>::const_iterator; + + explicit HandlerRegistryIterator(RawIterator raw) : raw_(std::move(raw)) {} + + CRU_DELETE_COPY(HandlerRegistryIterator) + CRU_DELETE_MOVE(HandlerRegistryIterator) + + ~HandlerRegistryIterator() = default; + + const std::function<T>& operator*() const { return raw_->second; } + const std::function<T>* operator->() const { return &raw_->second; } + + HandlerRegistryIterator& operator++() { + ++raw_; + return *this; + } + + HandlerRegistryIterator operator++(int) { + auto c = *this; + this->operator++(); + return c; + } + + bool operator==(const HandlerRegistryIterator<T>& other) const { + return this->raw_ == other.raw_; + } + + bool operator!=(const HandlerRegistryIterator<T>& other) const { + return !this->operator==(other); + } + + private: + RawIterator raw_; +}; + +template <typename T> +class HandlerRegistry final { + public: + HandlerRegistry() = default; + CRU_DEFAULT_COPY(HandlerRegistry) + CRU_DEFAULT_MOVE(HandlerRegistry) + ~HandlerRegistry() = default; + + public: + int AddHandler(std::function<T> handler) { + auto id = current_id_++; + handler_list_.push_back({id, std::move(handler)}); + } + + void RemoveHandler(int id) { + auto result = std::find_if(handler_list_.cbegin(), handler_list_.cend(), + [id](const std::pair<int, std::function<T>>& d) { + return d.first == id; + }); + if (result != handler_list_.cend()) { + handler_list_.erase(result); + } + } + + HandlerRegistryIterator<T> begin() const { + return HandlerRegistryIterator<T>(handler_list_.begin()); + } + + HandlerRegistryIterator<T> end() const { + return HandlerRegistryIterator<T>(handler_list_.begin()); + } + + private: + int current_id_ = 1; + std::vector<std::pair<int, std::function<T>>> handler_list_; +}; +} // namespace cru diff --git a/include/cru/common/Logger.hpp b/include/cru/common/Logger.hpp index 4ea17c09..daf2e7d2 100644 --- a/include/cru/common/Logger.hpp +++ b/include/cru/common/Logger.hpp @@ -40,6 +40,7 @@ class Logger : public Object { std::list<std::unique_ptr<ILogSource>> sources_; }; +// TODO: Remove argument evaluation in Debug. template <typename... TArgs> void Debug([[maybe_unused]] TArgs&&... args) { #ifdef CRU_DEBUG @@ -66,6 +67,7 @@ void Error(TArgs&&... args) { fmt::format(std::forward<TArgs>(args)...)); } +// TODO: Remove argument evaluation in Debug. template <typename... TArgs> void TagDebug([[maybe_unused]] std::u16string_view tag, [[maybe_unused]] TArgs&&... args) { diff --git a/include/cru/common/SelfResolvable.hpp b/include/cru/common/SelfResolvable.hpp index 94f3ae87..eaa4ce34 100644 --- a/include/cru/common/SelfResolvable.hpp +++ b/include/cru/common/SelfResolvable.hpp @@ -39,9 +39,27 @@ class SelfResolvable { SelfResolvable() : resolver_(new T*(static_cast<T*>(this))) {} SelfResolvable(const SelfResolvable&) = delete; SelfResolvable& operator=(const SelfResolvable&) = delete; - SelfResolvable(SelfResolvable&&) = delete; - SelfResolvable& operator=(SelfResolvable&&) = delete; - virtual ~SelfResolvable() { (*resolver_) = nullptr; } + + // Resolvers to old object will resolve to new object. + SelfResolvable(SelfResolvable&& other) + : resolver_(std::move(other.resolver_)) { + (*resolver_) = static_cast<T*>(this); + } + + // Old resolvers for this object will resolve to nullptr. + // Other's resolvers will now resolve to this. + SelfResolvable& operator=(SelfResolvable&& other) { + if (this != &other) { + (*resolver_) = nullptr; + resolver_ = std::move(other.resolver_); + (*resolver_) = static_cast<T*>(this); + } + return *this; + } + + virtual ~SelfResolvable() { + if (resolver_ != nullptr) (*resolver_) = nullptr; + } ObjectResolver<T> CreateResolver() { return ObjectResolver<T>(resolver_); } diff --git a/include/cru/common/StringUtil.hpp b/include/cru/common/StringUtil.hpp index 5dacfa12..62999d53 100644 --- a/include/cru/common/StringUtil.hpp +++ b/include/cru/common/StringUtil.hpp @@ -1,6 +1,10 @@ #pragma once #include "Base.hpp" +#include <functional> +#include <string> +#include <string_view> + namespace cru { using CodePoint = std::int32_t; constexpr CodePoint k_invalid_code_point = -1; @@ -124,4 +128,21 @@ void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string& str); std::string ToUtf8(std::u16string_view s); std::u16string ToUtf16(std::string_view s); + +// If given s is not a valid utf16 string, return value is UD. +bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position); + +// Return position after the character making predicate returns true or 0 if no +// character doing so. +gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position, + const std::function<bool(CodePoint)>& predicate); +// Return position before the character making predicate returns true or +// str.size() if no character doing so. +gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position, + const std::function<bool(CodePoint)>& predicate); + +gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position, + bool* is_space = nullptr); +gsl::index Utf16NextWord(std::u16string_view str, gsl::index position, + bool* is_space = nullptr); } // namespace cru |