#pragma once #include "base.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 { friend EventRevoker; protected: using EventHandlerToken = long; EventBase() : resolver_(new EventBase*(this)) {} 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); private: std::shared_ptr resolver_; }; } // namespace details // A copyable and movable event revoker. // Call function call operator to revoke the handler. class EventRevoker { friend class ::cru::details::EventBase; private: EventRevoker(const std::shared_ptr& resolver, details::EventBase::EventHandlerToken token) : weak_resolver_(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 { const auto true_resolver = weak_resolver_.lock(); // if true_resolver is nullptr, then the event has been destroyed. if (true_resolver) { (*true_resolver)->RemoveHandler(token_); } } private: std::weak_ptr weak_resolver_; details::EventBase::EventHandlerToken token_; }; inline EventRevoker details::EventBase::CreateRevoker(EventHandlerToken token) { return EventRevoker(resolver_, token); } // A non-copyable non-movable Event class. // It stores a list of event handlers. template class Event : public details::EventBase { public: using EventHandler = std::function; 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) { const auto token = current_token_++; handlers_.emplace(token, handler); return CreateRevoker(token); } EventRevoker AddHandler(EventHandler&& handler) { const auto token = current_token_++; handlers_.emplace(token, std::move(handler)); return CreateRevoker(token); } template EventRevoker AddHandler(FArg&& handler) { static_assert(std::is_invocable_v, "Handler not invocable."); const auto token = current_token_++; handlers_.emplace(token, EventHandler(std::forward(handler))); return CreateRevoker(token); } template void Raise(FArg&&... args) { // copy the handlers to a list, because the handler might be removed // during executing, and the handler with its data will be destroyed. // if the handler is a lambda with member data, then the member data // will be destroyed and result in seg fault. std::list handlers; for (const auto& [key, handler] : handlers_) handlers.push_back(handler); for (const auto& handler : handlers) handler(std::forward(args)...); } protected: void RemoveHandler(EventHandlerToken token) override { auto find_result = handlers_.find(token); if (find_result != handlers_.cend()) handlers_.erase(find_result); } private: std::map handlers_{}; EventHandlerToken current_token_ = 0; }; namespace details { struct EventRevokerGuardDestroyer { void operator()(EventRevoker* p) const { (*p)(); delete p; } }; } // namespace details class EventRevokerGuard { public: 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() const { return *revoker_; } private: std::unique_ptr revoker_; }; } // namespace cru