aboutsummaryrefslogtreecommitdiff
path: root/include/cru/common/Event.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/cru/common/Event.hpp')
-rw-r--r--include/cru/common/Event.hpp133
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