From 06d1d0442276a05b6caad6e3468f4afb1e8ee5df Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 28 Jun 2020 00:03:11 +0800 Subject: ... --- include/cru/common/Base.hpp | 47 +++++++++ include/cru/common/Bitmask.hpp | 42 ++++++++ include/cru/common/Event.hpp | 213 +++++++++++++++++++++++++++++++++++++++++ include/cru/common/Logger.hpp | 92 ++++++++++++++++++ include/cru/common/base.hpp | 47 --------- include/cru/common/bitmask.hpp | 42 -------- include/cru/common/event.hpp | 213 ----------------------------------------- include/cru/common/logger.hpp | 92 ------------------ 8 files changed, 394 insertions(+), 394 deletions(-) create mode 100644 include/cru/common/Base.hpp create mode 100644 include/cru/common/Bitmask.hpp create mode 100644 include/cru/common/Event.hpp create mode 100644 include/cru/common/Logger.hpp delete mode 100644 include/cru/common/base.hpp delete mode 100644 include/cru/common/bitmask.hpp delete mode 100644 include/cru/common/event.hpp delete mode 100644 include/cru/common/logger.hpp (limited to 'include/cru/common') diff --git a/include/cru/common/Base.hpp b/include/cru/common/Base.hpp new file mode 100644 index 00000000..ff7ab31e --- /dev/null +++ b/include/cru/common/Base.hpp @@ -0,0 +1,47 @@ +#pragma once +#include "PreConfig.hpp" + +#include + +#include + +#define CRU_UNUSED(entity) static_cast(entity); + +#define CRU_DEFAULT_COPY(classname) \ + classname(const classname&) = default; \ + classname& operator=(const classname&) = default; + +#define CRU_DEFAULT_MOVE(classname) \ + classname(classname&&) = default; \ + classname& operator=(classname&&) = default; + +#define CRU_DELETE_COPY(classname) \ + classname(const classname&) = delete; \ + classname& operator=(const classname&) = delete; + +#define CRU_DELETE_MOVE(classname) \ + classname(classname&&) = delete; \ + classname& operator=(classname&&) = delete; + +namespace cru { +class Object { + public: + Object() = default; + CRU_DEFAULT_COPY(Object) + CRU_DEFAULT_MOVE(Object) + virtual ~Object() = default; +}; + +struct Interface { + Interface() = default; + CRU_DELETE_COPY(Interface) + CRU_DELETE_MOVE(Interface) + virtual ~Interface() = default; +}; + +[[noreturn]] inline void UnreachableCode() { + throw std::runtime_error("Unreachable code."); +} + +using Index = gsl::index; +} // namespace cru diff --git a/include/cru/common/Bitmask.hpp b/include/cru/common/Bitmask.hpp new file mode 100644 index 00000000..ddfdc86b --- /dev/null +++ b/include/cru/common/Bitmask.hpp @@ -0,0 +1,42 @@ +#pragma once +#include "Base.hpp" + +namespace cru { +template +struct Bitmask final { + using Underlying = TUnderlying; + + constexpr Bitmask() : value(0) {} + constexpr explicit Bitmask(TUnderlying value) : value(value) {} + + CRU_DEFAULT_COPY(Bitmask) + CRU_DEFAULT_MOVE(Bitmask) + + ~Bitmask() = default; + + Bitmask operator|(Bitmask rhs) const { return Bitmask(value | rhs.value); } + Bitmask operator&(Bitmask rhs) const { return Bitmask(value & rhs.value); } + Bitmask operator^(Bitmask rhs) const { return Bitmask(value ^ rhs.value); } + Bitmask operator~() const { return Bitmask(~value); } + Bitmask& operator|=(Bitmask rhs) { + value |= rhs.value; + return *this; + } + Bitmask& operator&=(Bitmask rhs) { + value &= rhs.value; + return *this; + } + Bitmask& operator^=(Bitmask rhs) { + value ^= rhs.value; + return *this; + } + + bool operator==(Bitmask rhs) const { return this->value == rhs.value; } + bool operator!=(Bitmask rhs) const { return this->value != rhs.value; } + + explicit operator TUnderlying() const { return value; } + explicit operator bool() const { return value != 0; } + + TUnderlying value; +}; +} // namespace cru diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp new file mode 100644 index 00000000..377ca7f3 --- /dev/null +++ b/include/cru/common/Event.hpp @@ -0,0 +1,213 @@ +#pragma once +#include "Base.hpp" + +#include "SelfResolvable.hpp" + +#include +#include +#include +#include +#include + +namespace cru { +class EventRevoker; + +namespace details { +// Base class of all Event. +// It erases event args types and provides a +// unified form to create event revoker and +// revoke(remove) handler. +class EventBase : public SelfResolvable { + friend EventRevoker; + + protected: + using EventHandlerToken = long; + + EventBase() {} + EventBase(const EventBase& other) = delete; + EventBase(EventBase&& other) = delete; + EventBase& operator=(const EventBase& other) = delete; + EventBase& operator=(EventBase&& other) = delete; + virtual ~EventBase() = default; + + // Remove the handler with the given token. If the token + // corresponds to no handler (which might have be revoked + // before), then nothing will be done. + virtual void RemoveHandler(EventHandlerToken token) = 0; + + // Create a revoker with the given token. + inline EventRevoker CreateRevoker(EventHandlerToken token); +}; +} // namespace details + +// A non-copyable and movable event revoker. +// Call function call operator to revoke the handler. +class EventRevoker { + friend details::EventBase; + + private: + EventRevoker(ObjectResolver&& resolver, + details::EventBase::EventHandlerToken token) + : resolver_(std::move(resolver)), token_(token) {} + + public: + EventRevoker(const EventRevoker& other) = default; + EventRevoker(EventRevoker&& other) = default; + EventRevoker& operator=(const EventRevoker& other) = default; + EventRevoker& operator=(EventRevoker&& other) = default; + ~EventRevoker() = default; + + // Revoke the registered handler. If the event has already + // been destroyed, then nothing will be done. If one of the + // copies calls this, then other copies's calls will have no + // effect. (They have the same token.) + void operator()() const { + if (const auto event = resolver_.Resolve()) { + event->RemoveHandler(token_); + } + } + + private: + ObjectResolver resolver_; + details::EventBase::EventHandlerToken token_; +}; + +inline EventRevoker details::EventBase::CreateRevoker(EventHandlerToken token) { + return EventRevoker(CreateResolver(), token); +} + +// int -> int +// Point -> const Point& +// int& -> int& +template +using DeducedEventArgs = std::conditional_t< + std::is_lvalue_reference_v, TRaw, + std::conditional_t, TRaw, const TRaw&>>; + +// 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 +struct IEvent { + public: + using EventArgs = DeducedEventArgs; + using EventHandler = std::function; + + protected: + IEvent() = default; + IEvent(const IEvent& other) = delete; + IEvent(IEvent&& other) = delete; + IEvent& operator=(const IEvent& other) = delete; + IEvent& operator=(IEvent&& other) = delete; + ~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; +}; + +// A non-copyable non-movable Event class. +// It stores a list of event handlers. +template +class Event : public details::EventBase, public IEvent { + using typename IEvent::EventHandler; + + private: + struct HandlerData { + HandlerData(EventHandlerToken token, EventHandler handler) + : token(token), handler(handler) {} + EventHandlerToken token; + EventHandler handler; + }; + + public: + Event() = default; + Event(const Event&) = delete; + Event& operator=(const Event&) = delete; + Event(Event&&) = delete; + Event& operator=(Event&&) = delete; + ~Event() = default; + + EventRevoker AddHandler(const EventHandler& handler) override { + const auto token = current_token_++; + this->handler_data_list_.emplace_back(token, handler); + return CreateRevoker(token); + } + + EventRevoker AddHandler(EventHandler&& handler) override { + const auto token = current_token_++; + this->handler_data_list_.emplace_back(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::EventArgs args) { + std::vector 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); + } + } + + protected: + void RemoveHandler(EventHandlerToken token) override { + const auto find_result = std::find_if( + this->handler_data_list_.cbegin(), this->handler_data_list_.cend(), + [token](const HandlerData& data) { return data.token == token; }); + if (find_result != this->handler_data_list_.cend()) { + this->handler_data_list_.erase(find_result); + } + } + + private: + std::vector handler_data_list_; + EventHandlerToken current_token_ = 0; +}; + +namespace details { +struct EventRevokerDestroyer { + void operator()(EventRevoker* p) { + (*p)(); + delete p; + } +}; +} // namespace details + +class EventRevokerGuard { + public: + EventRevokerGuard() = default; + explicit EventRevokerGuard(EventRevoker&& revoker) + : revoker_(new EventRevoker(std::move(revoker))) {} + EventRevokerGuard(const EventRevokerGuard& other) = delete; + EventRevokerGuard(EventRevokerGuard&& other) = default; + EventRevokerGuard& operator=(const EventRevokerGuard& other) = delete; + EventRevokerGuard& operator=(EventRevokerGuard&& other) = default; + ~EventRevokerGuard() = default; + + EventRevoker Get() { + // revoker is only null when this is moved + // you shouldn't use a moved instance + Expects(revoker_); + return *revoker_; + } + + void Release() { revoker_.release(); } + + void Reset(EventRevoker&& revoker) { + revoker_.reset(new EventRevoker(std::move(revoker))); + } + + private: + std::unique_ptr revoker_; +}; // namespace cru +} // namespace cru diff --git a/include/cru/common/Logger.hpp b/include/cru/common/Logger.hpp new file mode 100644 index 00000000..bd16678b --- /dev/null +++ b/include/cru/common/Logger.hpp @@ -0,0 +1,92 @@ +#pragma once +#include "cru/common/Base.hpp" + +#include +#include +#include +#include +#include +#include + +namespace cru::log { + +enum class LogLevel { Debug, Info, Warn, Error }; + +struct ILogSource : virtual Interface { + // Write the string s. LogLevel is just a helper. It has no effect on the + // content to write. + virtual void Write(LogLevel level, const std::string_view& s) = 0; +}; + +class StdioLogSource : public virtual ILogSource { + public: + StdioLogSource() = default; + + CRU_DELETE_COPY(StdioLogSource) + CRU_DELETE_MOVE(StdioLogSource) + + ~StdioLogSource() override = default; + + void Write(LogLevel level, const std::string_view& s) override { + // TODO: Emmm... Since it is buggy to use narrow char in UTF-8 on Windows. I + // think this implementation might be broken. (However, I didn't test it.) + // Maybe, I should detect Windows and use wide char (And I didn't test this + // either) or other more complicated implementation. Currently, I settled + // with this. + if (level == LogLevel::Error) { + std::cerr << s; + } else { + std::cout << s; + } + } +}; + +class Logger : public Object { + public: + static Logger* GetInstance(); + + public: + Logger() = default; + + CRU_DELETE_COPY(Logger) + CRU_DELETE_MOVE(Logger) + + ~Logger() override = default; + + public: + void AddSource(std::unique_ptr source); + void RemoveSource(ILogSource* source); + + public: + void Log(LogLevel level, const std::string_view& s); + + public: + std::list> sources_; +}; + +template +void Debug([[maybe_unused]] TArgs&&... args) { +#ifdef CRU_DEBUG + Logger::GetInstance()->Log(LogLevel::Debug, + fmt::format(std::forward(args)...)); +#endif +} + +template +void Info(TArgs&&... args) { + Logger::GetInstance()->Log(LogLevel::Info, + fmt::format(std::forward(args)...)); +} + +template +void Warn(TArgs&&... args) { + Logger::GetInstance()->Log(LogLevel::Warn, + fmt::format(std::forward(args)...)); +} + +template +void Error(TArgs&&... args) { + Logger::GetInstance()->Log(LogLevel::Error, + fmt::format(std::forward(args)...)); +} +} // namespace cru::log diff --git a/include/cru/common/base.hpp b/include/cru/common/base.hpp deleted file mode 100644 index ff7ab31e..00000000 --- a/include/cru/common/base.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "PreConfig.hpp" - -#include - -#include - -#define CRU_UNUSED(entity) static_cast(entity); - -#define CRU_DEFAULT_COPY(classname) \ - classname(const classname&) = default; \ - classname& operator=(const classname&) = default; - -#define CRU_DEFAULT_MOVE(classname) \ - classname(classname&&) = default; \ - classname& operator=(classname&&) = default; - -#define CRU_DELETE_COPY(classname) \ - classname(const classname&) = delete; \ - classname& operator=(const classname&) = delete; - -#define CRU_DELETE_MOVE(classname) \ - classname(classname&&) = delete; \ - classname& operator=(classname&&) = delete; - -namespace cru { -class Object { - public: - Object() = default; - CRU_DEFAULT_COPY(Object) - CRU_DEFAULT_MOVE(Object) - virtual ~Object() = default; -}; - -struct Interface { - Interface() = default; - CRU_DELETE_COPY(Interface) - CRU_DELETE_MOVE(Interface) - virtual ~Interface() = default; -}; - -[[noreturn]] inline void UnreachableCode() { - throw std::runtime_error("Unreachable code."); -} - -using Index = gsl::index; -} // namespace cru diff --git a/include/cru/common/bitmask.hpp b/include/cru/common/bitmask.hpp deleted file mode 100644 index ddfdc86b..00000000 --- a/include/cru/common/bitmask.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "Base.hpp" - -namespace cru { -template -struct Bitmask final { - using Underlying = TUnderlying; - - constexpr Bitmask() : value(0) {} - constexpr explicit Bitmask(TUnderlying value) : value(value) {} - - CRU_DEFAULT_COPY(Bitmask) - CRU_DEFAULT_MOVE(Bitmask) - - ~Bitmask() = default; - - Bitmask operator|(Bitmask rhs) const { return Bitmask(value | rhs.value); } - Bitmask operator&(Bitmask rhs) const { return Bitmask(value & rhs.value); } - Bitmask operator^(Bitmask rhs) const { return Bitmask(value ^ rhs.value); } - Bitmask operator~() const { return Bitmask(~value); } - Bitmask& operator|=(Bitmask rhs) { - value |= rhs.value; - return *this; - } - Bitmask& operator&=(Bitmask rhs) { - value &= rhs.value; - return *this; - } - Bitmask& operator^=(Bitmask rhs) { - value ^= rhs.value; - return *this; - } - - bool operator==(Bitmask rhs) const { return this->value == rhs.value; } - bool operator!=(Bitmask rhs) const { return this->value != rhs.value; } - - explicit operator TUnderlying() const { return value; } - explicit operator bool() const { return value != 0; } - - TUnderlying value; -}; -} // namespace cru diff --git a/include/cru/common/event.hpp b/include/cru/common/event.hpp deleted file mode 100644 index 377ca7f3..00000000 --- a/include/cru/common/event.hpp +++ /dev/null @@ -1,213 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include "SelfResolvable.hpp" - -#include -#include -#include -#include -#include - -namespace cru { -class EventRevoker; - -namespace details { -// Base class of all Event. -// It erases event args types and provides a -// unified form to create event revoker and -// revoke(remove) handler. -class EventBase : public SelfResolvable { - friend EventRevoker; - - protected: - using EventHandlerToken = long; - - EventBase() {} - EventBase(const EventBase& other) = delete; - EventBase(EventBase&& other) = delete; - EventBase& operator=(const EventBase& other) = delete; - EventBase& operator=(EventBase&& other) = delete; - virtual ~EventBase() = default; - - // Remove the handler with the given token. If the token - // corresponds to no handler (which might have be revoked - // before), then nothing will be done. - virtual void RemoveHandler(EventHandlerToken token) = 0; - - // Create a revoker with the given token. - inline EventRevoker CreateRevoker(EventHandlerToken token); -}; -} // namespace details - -// A non-copyable and movable event revoker. -// Call function call operator to revoke the handler. -class EventRevoker { - friend details::EventBase; - - private: - EventRevoker(ObjectResolver&& resolver, - details::EventBase::EventHandlerToken token) - : resolver_(std::move(resolver)), token_(token) {} - - public: - EventRevoker(const EventRevoker& other) = default; - EventRevoker(EventRevoker&& other) = default; - EventRevoker& operator=(const EventRevoker& other) = default; - EventRevoker& operator=(EventRevoker&& other) = default; - ~EventRevoker() = default; - - // Revoke the registered handler. If the event has already - // been destroyed, then nothing will be done. If one of the - // copies calls this, then other copies's calls will have no - // effect. (They have the same token.) - void operator()() const { - if (const auto event = resolver_.Resolve()) { - event->RemoveHandler(token_); - } - } - - private: - ObjectResolver resolver_; - details::EventBase::EventHandlerToken token_; -}; - -inline EventRevoker details::EventBase::CreateRevoker(EventHandlerToken token) { - return EventRevoker(CreateResolver(), token); -} - -// int -> int -// Point -> const Point& -// int& -> int& -template -using DeducedEventArgs = std::conditional_t< - std::is_lvalue_reference_v, TRaw, - std::conditional_t, TRaw, const TRaw&>>; - -// 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 -struct IEvent { - public: - using EventArgs = DeducedEventArgs; - using EventHandler = std::function; - - protected: - IEvent() = default; - IEvent(const IEvent& other) = delete; - IEvent(IEvent&& other) = delete; - IEvent& operator=(const IEvent& other) = delete; - IEvent& operator=(IEvent&& other) = delete; - ~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; -}; - -// A non-copyable non-movable Event class. -// It stores a list of event handlers. -template -class Event : public details::EventBase, public IEvent { - using typename IEvent::EventHandler; - - private: - struct HandlerData { - HandlerData(EventHandlerToken token, EventHandler handler) - : token(token), handler(handler) {} - EventHandlerToken token; - EventHandler handler; - }; - - public: - Event() = default; - Event(const Event&) = delete; - Event& operator=(const Event&) = delete; - Event(Event&&) = delete; - Event& operator=(Event&&) = delete; - ~Event() = default; - - EventRevoker AddHandler(const EventHandler& handler) override { - const auto token = current_token_++; - this->handler_data_list_.emplace_back(token, handler); - return CreateRevoker(token); - } - - EventRevoker AddHandler(EventHandler&& handler) override { - const auto token = current_token_++; - this->handler_data_list_.emplace_back(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::EventArgs args) { - std::vector 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); - } - } - - protected: - void RemoveHandler(EventHandlerToken token) override { - const auto find_result = std::find_if( - this->handler_data_list_.cbegin(), this->handler_data_list_.cend(), - [token](const HandlerData& data) { return data.token == token; }); - if (find_result != this->handler_data_list_.cend()) { - this->handler_data_list_.erase(find_result); - } - } - - private: - std::vector handler_data_list_; - EventHandlerToken current_token_ = 0; -}; - -namespace details { -struct EventRevokerDestroyer { - void operator()(EventRevoker* p) { - (*p)(); - delete p; - } -}; -} // namespace details - -class EventRevokerGuard { - public: - EventRevokerGuard() = default; - explicit EventRevokerGuard(EventRevoker&& revoker) - : revoker_(new EventRevoker(std::move(revoker))) {} - EventRevokerGuard(const EventRevokerGuard& other) = delete; - EventRevokerGuard(EventRevokerGuard&& other) = default; - EventRevokerGuard& operator=(const EventRevokerGuard& other) = delete; - EventRevokerGuard& operator=(EventRevokerGuard&& other) = default; - ~EventRevokerGuard() = default; - - EventRevoker Get() { - // revoker is only null when this is moved - // you shouldn't use a moved instance - Expects(revoker_); - return *revoker_; - } - - void Release() { revoker_.release(); } - - void Reset(EventRevoker&& revoker) { - revoker_.reset(new EventRevoker(std::move(revoker))); - } - - private: - std::unique_ptr revoker_; -}; // namespace cru -} // namespace cru diff --git a/include/cru/common/logger.hpp b/include/cru/common/logger.hpp deleted file mode 100644 index bd16678b..00000000 --- a/include/cru/common/logger.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once -#include "cru/common/Base.hpp" - -#include -#include -#include -#include -#include -#include - -namespace cru::log { - -enum class LogLevel { Debug, Info, Warn, Error }; - -struct ILogSource : virtual Interface { - // Write the string s. LogLevel is just a helper. It has no effect on the - // content to write. - virtual void Write(LogLevel level, const std::string_view& s) = 0; -}; - -class StdioLogSource : public virtual ILogSource { - public: - StdioLogSource() = default; - - CRU_DELETE_COPY(StdioLogSource) - CRU_DELETE_MOVE(StdioLogSource) - - ~StdioLogSource() override = default; - - void Write(LogLevel level, const std::string_view& s) override { - // TODO: Emmm... Since it is buggy to use narrow char in UTF-8 on Windows. I - // think this implementation might be broken. (However, I didn't test it.) - // Maybe, I should detect Windows and use wide char (And I didn't test this - // either) or other more complicated implementation. Currently, I settled - // with this. - if (level == LogLevel::Error) { - std::cerr << s; - } else { - std::cout << s; - } - } -}; - -class Logger : public Object { - public: - static Logger* GetInstance(); - - public: - Logger() = default; - - CRU_DELETE_COPY(Logger) - CRU_DELETE_MOVE(Logger) - - ~Logger() override = default; - - public: - void AddSource(std::unique_ptr source); - void RemoveSource(ILogSource* source); - - public: - void Log(LogLevel level, const std::string_view& s); - - public: - std::list> sources_; -}; - -template -void Debug([[maybe_unused]] TArgs&&... args) { -#ifdef CRU_DEBUG - Logger::GetInstance()->Log(LogLevel::Debug, - fmt::format(std::forward(args)...)); -#endif -} - -template -void Info(TArgs&&... args) { - Logger::GetInstance()->Log(LogLevel::Info, - fmt::format(std::forward(args)...)); -} - -template -void Warn(TArgs&&... args) { - Logger::GetInstance()->Log(LogLevel::Warn, - fmt::format(std::forward(args)...)); -} - -template -void Error(TArgs&&... args) { - Logger::GetInstance()->Log(LogLevel::Error, - fmt::format(std::forward(args)...)); -} -} // namespace cru::log -- cgit v1.2.3