diff options
author | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
commit | dfe62dcf8bcefc523b466e127c3edc4dc2756629 (patch) | |
tree | 1c751a14ba0da07ca2ff805633f97568060aa4c9 /include/cru/base/Event2.h | |
parent | f51eb955e188858272230a990565931e7403f23b (diff) | |
download | cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.gz cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.bz2 cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.zip |
Rename common to base.
Diffstat (limited to 'include/cru/base/Event2.h')
-rw-r--r-- | include/cru/base/Event2.h | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/include/cru/base/Event2.h b/include/cru/base/Event2.h new file mode 100644 index 00000000..891b314f --- /dev/null +++ b/include/cru/base/Event2.h @@ -0,0 +1,200 @@ +#pragma once + +#include <cru/base/Bitmask.h> +#include <cru/base/SelfResolvable.h> + +#include <cstddef> +#include <functional> +#include <type_traits> +#include <utility> +#include <vector> + +namespace cru { +class Event2Base { + public: + virtual ~Event2Base() = default; + + virtual void RevokeHandler(int token_value) = 0; +}; + +template <typename TArgument, typename TResult> +class EventContext { + public: + EventContext() : argument_(), result_() {} + explicit EventContext(TArgument argument) + : argument_(std::move(argument)), result_() {} + EventContext(TArgument argument, TResult result) + : argument_(std::move(argument)), result_(std::move(result)) {} + + TArgument& GetArgument() { return argument_; } + const TArgument& GetArgument() const { return argument_; } + + TResult& GetResult() { return result_; } + const TResult& GetResult() const { return result_; } + void SetResult(const TResult& result) { result_ = result; } + void SetResult(TResult&& result) { result_ = std::move(result); } + TResult TakeResult() { return std::move(result_); } + + bool GetStopHandling() const { return stop_handling_; } + void SetStopHandling(bool stop = true) { stop_handling_ = stop; } + + private: + TArgument argument_; + TResult result_; + bool stop_handling_ = false; +}; + +template <typename TArgument, typename TResult> +class Event2; + +template <typename T> +constexpr bool is_event2_v = false; + +template <typename TArgument, typename TResult> +constexpr bool is_event2_v<Event2<TArgument, TResult>> = true; + +class EventHandlerToken { + public: + EventHandlerToken(ObjectResolver<Event2Base> event_resolver, int token_value) + : event_resolver_(std::move(event_resolver)), token_value_(token_value) {} + + ObjectResolver<Event2Base> GetEventResolver() const { + return event_resolver_; + } + int GetTokenValue() const { return token_value_; } + inline void RevokeHandler() const; + + private: + ObjectResolver<Event2Base> event_resolver_; + int token_value_; +}; + +namespace details { +struct Event2BehaviorFlagTag {}; +} // namespace details +using Event2BehaviorFlag = Bitmask<details::Event2BehaviorFlagTag>; + +struct Event2BehaviorFlags { + /** + * @brief Make a copy of handler list before invoking handlers. So the event + * object or its owner can be destroyed during running handlers. + */ + static constexpr Event2BehaviorFlag CopyHandlers = + Event2BehaviorFlag::FromOffset(1); +}; + +template <typename TArgument = std::nullptr_t, + typename TResult = std::nullptr_t> +class Event2 : public Event2Base, + public SelfResolvable<Event2<TArgument, TResult>> { + public: + using HandlerToken = EventHandlerToken; + using Context = EventContext<TArgument, TResult>; + using Handler = std::function<void(Context*)>; + using SpyOnlyHandler = std::function<void()>; + + template <typename TFunc> + static std::enable_if_t<std::invocable<TFunc>, Handler> WrapAsHandler( + TFunc&& handler) { + return Handler([h = std::forward<TFunc>(handler)](Context*) { h(); }); + } + + template <typename TFunc> + static std::enable_if_t<std::invocable<TFunc, Context*>, Handler> + WrapAsHandler(TFunc&& handler) { + return Handler(std::forward<TFunc>(handler)); + } + + private: + struct HandlerData { + int token_value; + Handler handler; + }; + + public: + explicit Event2(Event2BehaviorFlag flags = {}) : flags_(flags) {} + Event2(const Event2&) = delete; + Event2(Event2&&) = delete; + Event2& operator=(const Event2&) = delete; + Event2& operator=(Event2&&) = delete; + ~Event2() override = default; + + public: + template <typename TFunc> + HandlerToken AddHandler(TFunc&& handler) { + auto token = this->current_token_value_++; + auto real_handler = WrapAsHandler(std::forward<TFunc>(handler)); + HandlerData handler_data{token, std::move(real_handler)}; + this->handlers_.push_back(std::move(handler_data)); + return HandlerToken(this->CreateResolver(), token); + } + + void RevokeHandler(int token_value) override { + auto iter = this->handlers_.cbegin(); + auto end = this->handlers_.cend(); + for (; iter != end; ++iter) { + if (iter->token_value == token_value) { + this->handlers_.erase(iter); + break; + } + } + } + + void RevokeHandler(const HandlerToken& token) { + return RevokeHandler(token.GetTokenValue()); + } + + TResult Raise() { + Context context; + RunInContext(&context); + return context.TakeResult(); + } + + TResult Raise(TArgument argument) { + Context context(std::move(argument)); + RunInContext(&context); + return context.TakeResult(); + } + + TResult Raise(TArgument argument, TResult result) { + Context context(std::move(argument), std::move(result)); + RunInContext(&context); + return context.TakeResult(); + } + + private: + void RunInContext(Context* context) { + if (this->flags_ & Event2BehaviorFlags::CopyHandlers) { + std::vector<Handler> handlers_copy; + for (const auto& handler : this->handlers_) { + handlers_copy.push_back(handler.handler); + } + for (const auto& handler : handlers_copy) { + if (context->GetStopHandling()) { + break; + } + handler(context); + } + } else { + for (const auto& handler : this->handlers_) { + if (context->GetStopHandling()) { + break; + } + handler.handler(context); + } + } + } + + private: + int current_token_value_ = 1; + std::vector<HandlerData> handlers_; + Event2BehaviorFlag flags_; +}; + +inline void EventHandlerToken::RevokeHandler() const { + auto event = this->event_resolver_.Resolve(); + if (event) { + event->RevokeHandler(this->token_value_); + } +} +} // namespace cru |