/* * SubscriptionRegistry.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once VCMI_LIB_NAMESPACE_BEGIN class Environment; namespace events { class EventBus; class DLL_LINKAGE EventSubscription : public boost::noncopyable { public: virtual ~EventSubscription() = default; }; template class SubscriptionRegistry : public boost::noncopyable { public: using PreHandler = std::function; using ExecHandler = std::function; using PostHandler = std::function; using BusTag = const void *; std::unique_ptr subscribeBefore(BusTag tag, PreHandler && handler) { std::unique_lock lock(mutex); auto storage = std::make_shared(std::move(handler)); preHandlers[tag].push_back(storage); return std::make_unique(tag, storage); } std::unique_ptr subscribeAfter(BusTag tag, PostHandler && handler) { std::unique_lock lock(mutex); auto storage = std::make_shared(std::move(handler)); postHandlers[tag].push_back(storage); return std::make_unique(tag, storage); } void executeEvent(const EventBus * bus, E & event, const ExecHandler & execHandler) { std::shared_lock lock(mutex); { auto it = preHandlers.find(bus); if(it != std::end(preHandlers)) { for(auto & h : it->second) (*h)(event); } } if(event.isEnabled()) { if(execHandler) execHandler(event); auto it = postHandlers.find(bus); if(it != std::end(postHandlers)) { for(auto & h : it->second) (*h)(event); } } } private: template class HandlerStorage { public: explicit HandlerStorage(T && handler_) : handler(handler_) { } STRONG_INLINE void operator()(E & event) { handler(event); } private: T handler; }; using PreHandlerStorage = HandlerStorage; using PostHandlerStorage = HandlerStorage; class PreSubscription : public EventSubscription { public: PreSubscription(BusTag tag_, std::shared_ptr handler_) : handler(handler_), tag(tag_) { } virtual ~PreSubscription() { auto registry = E::getRegistry(); registry->unsubscribe(tag, handler, registry->preHandlers); } private: BusTag tag; std::shared_ptr handler; }; class PostSubscription : public EventSubscription { public: PostSubscription(BusTag tag_, std::shared_ptr handler_) : handler(handler_), tag(tag_) { } virtual ~PostSubscription() { auto registry = E::getRegistry(); registry->unsubscribe(tag, handler, registry->postHandlers); } private: BusTag tag; std::shared_ptr handler; }; std::shared_mutex mutex; std::map>> preHandlers; std::map>> postHandlers; template void unsubscribe(BusTag tag, T what, std::map> & from) { std::unique_lock lock(mutex); auto it = from.find(tag); if(it != std::end(from)) { it->second -= what; if(it->second.empty()) { from.erase(tag); } } } }; } VCMI_LIB_NAMESPACE_END