/******************************************************************************** * Copyright (C) 2014-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * * * This software is distributed under the terms of the * * GNU Lesser General Public Licence (LGPL) version 3, * * copied verbatim in the file "LICENSE" * ********************************************************************************/ #ifndef FAIR_MQ_EVENTMANAGER_H #define FAIR_MQ_EVENTMANAGER_H #include #include #include #include #include #include #include #include #include #include namespace fair::mq { // Inherit from this base event type to create custom event types template struct Event { using KeyType = K; }; /** * @class EventManager EventManager.h * @brief Manages event callbacks from different subscribers * * The event manager stores a set of callbacks and associates them with * events depending on the callback signature. The first callback * argument must be of a special key type determined by the event type. * * Callbacks can be subscribed/unsubscribed based on a subscriber id, * the event type, and the callback signature. * * Events can be emitted based on event type and callback signature. * * The event manager is thread-safe. */ class EventManager { public: // Clang 3.4-3.8 has a bug and cannot properly deal with the following template alias. // Therefore, we leave them here commented out for now. // template // using Callback = std::function; template using Signal = boost::signals2::signal; template auto Subscribe(const std::string& subscriber, std::function callback) -> void; template auto Unsubscribe(const std::string& subscriber) -> void { const std::type_index event_type_index{typeid(E)}; const std::type_index callback_type_index{ typeid(std::function)}; const auto signalsKey = std::make_pair(event_type_index, callback_type_index); const auto connectionsKey = std::make_pair(subscriber, signalsKey); std::lock_guard lock{fMutex}; fConnections.at(connectionsKey).disconnect(); fConnections.erase(connectionsKey); } template auto Emit(typename E::KeyType key, Args... args) const -> void { const std::type_index event_type_index{typeid(E)}; const std::type_index callback_type_index{ typeid(std::function)}; const auto signalsKey = std::make_pair(event_type_index, callback_type_index); (*GetSignal(signalsKey))(key, std::forward(args)...); } private: using SignalsKey = std::pair; // event , callback using SignalsValue = boost::any; using SignalsMap = std::unordered_map>; mutable SignalsMap fSignals; using ConnectionsKey = std::pair; // subscriber , event/callback using ConnectionsValue = boost::signals2::connection; using ConnectionsMap = std::unordered_map>; ConnectionsMap fConnections; mutable std::mutex fMutex; template auto GetSignal(const SignalsKey& key) const -> std::shared_ptr>; }; /* class EventManager */ struct PropertyChangeAsString : Event {}; template auto EventManager::GetSignal(const SignalsKey& key) const -> std::shared_ptr> { std::lock_guard lock{fMutex}; if (fSignals.find(key) == fSignals.end()) { // wrapper is needed because boost::signals2::signal is neither copyable nor movable // and I don't know how else to insert it into the map auto signal = std::make_shared>(); fSignals.insert(std::make_pair(key, signal)); } return boost::any_cast>>(fSignals.at(key)); } template auto EventManager::Subscribe(const std::string& subscriber, std::function callback) -> void { const std::type_index event_type_index{typeid(E)}; const std::type_index callback_type_index{ typeid(std::function)}; const auto signalsKey = std::make_pair(event_type_index, callback_type_index); const auto connectionsKey = std::make_pair(subscriber, signalsKey); const auto connection = GetSignal(signalsKey)->connect(callback); { std::lock_guard lock{fMutex}; if (fConnections.find(connectionsKey) != fConnections.end()) { fConnections.at(connectionsKey).disconnect(); fConnections.erase(connectionsKey); } fConnections.insert({connectionsKey, connection}); } } extern template std::shared_ptr< fair::mq::EventManager::Signal> fair::mq::EventManager::GetSignal( const std::pair& key) const; extern template void fair::mq::EventManager::Subscribe( const std::string& subscriber, std::function); } // namespace fair::mq #endif /* FAIR_MQ_EVENTMANAGER_H */