diff options
Diffstat (limited to 'include/cru/common/Event.hpp')
-rw-r--r-- | include/cru/common/Event.hpp | 133 |
1 files changed, 101 insertions, 32 deletions
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 |