Source · fairmq/
EventManager.hpublic header
Defines class fair::mq::EventManager, struct fair::mq::Event, struct fair::mq::PropertyChangeAsString.
1/********************************************************************************2 * Copyright (C) 2014-2025 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *3 * *4 * This software is distributed under the terms of the *5 * GNU Lesser General Public Licence (LGPL) version 3, *6 * copied verbatim in the file "LICENSE" *7 ********************************************************************************/8 9#ifndef FAIR_MQ_EVENTMANAGER_H10#define FAIR_MQ_EVENTMANAGER_H11 12#include <memory>13#include <mutex>14#include <string>15#include <typeindex>16#include <unordered_map>17#include <utility>18#include <functional>19 20#include <boost/any.hpp>21#include <boost/functional/hash.hpp>22#include <boost/signals2.hpp>23 24namespace fair::mq25{26 27// Inherit from this base event type to create custom event types28template<typename K>29struct Event30{31 using KeyType = K;32};33 34/**35 * @class EventManager EventManager.h <fairmq/EventManager.h>36 * @brief Manages event callbacks from different subscribers37 *38 * The event manager stores a set of callbacks and associates them with39 * events depending on the callback signature. The first callback40 * argument must be of a special key type determined by the event type.41 *42 * Callbacks can be subscribed/unsubscribed based on a subscriber id,43 * the event type, and the callback signature.44 *45 * Events can be emitted based on event type and callback signature.46 *47 * The event manager is thread-safe.48 */49class EventManager50{51 public:52 // Clang 3.4-3.8 has a bug and cannot properly deal with the following template alias.53 // Therefore, we leave them here commented out for now.54 // template<typename E, typename ...Args>55 // using Callback = std::function<void(typename E::KeyType, Args...)>;56 57 template<typename E, typename ...Args>58 using Signal = boost::signals2::signal<void(typename E::KeyType, Args...)>;59 60 template<typename E, typename... Args>61 auto Subscribe(const std::string& subscriber, std::function<void(typename E::KeyType, Args...)> callback) -> void;62 63 template<typename E, typename ...Args>64 auto Unsubscribe(const std::string& subscriber) -> void65 {66 const std::type_index event_type_index{typeid(E)};67 const std::type_index callback_type_index{typeid(std::function<void(typename E::KeyType, Args...)>)};68 const auto signalsKey = std::make_pair(event_type_index, callback_type_index);69 const auto connectionsKey = std::make_pair(subscriber, signalsKey);70 71 std::lock_guard<std::mutex> lock{fMutex};72 73 fConnections.at(connectionsKey).disconnect();74 fConnections.erase(connectionsKey);75 }76 77 template<typename E, typename ...Args>78 auto Emit(typename E::KeyType key, Args... args) const -> void79 {80 const std::type_index event_type_index{typeid(E)};81 const std::type_index callback_type_index{typeid(std::function<void(typename E::KeyType, Args...)>)};82 const auto signalsKey = std::make_pair(event_type_index, callback_type_index);83 84 (*GetSignal<E, Args...>(signalsKey))(key, std::forward<Args>(args)...);85 }86 87 private:88 using SignalsKey = std::pair<std::type_index, std::type_index>;89 // event , callback90 using SignalsValue = boost::any;91 using SignalsMap = std::unordered_map<SignalsKey, SignalsValue, boost::hash<SignalsKey>>;92 mutable SignalsMap fSignals;93 94 using ConnectionsKey = std::pair<std::string, SignalsKey>;95 // subscriber , event/callback96 using ConnectionsValue = boost::signals2::connection;97 using ConnectionsMap = std::unordered_map<ConnectionsKey, ConnectionsValue, boost::hash<ConnectionsKey>>;98 ConnectionsMap fConnections;99 100 mutable std::mutex fMutex;101 102 template<typename E, typename ...Args>103 auto GetSignal(const SignalsKey& key) const -> std::shared_ptr<Signal<E, Args...>>;104}; /* class EventManager */105 106struct PropertyChangeAsString : Event<std::string> {};107 108template<typename E, typename... Args>109auto EventManager::GetSignal(const SignalsKey& key) const -> std::shared_ptr<Signal<E, Args...>>110{111 std::lock_guard<std::mutex> lock{fMutex};112 113 if (fSignals.find(key) == fSignals.end()) {114 // wrapper is needed because boost::signals2::signal is neither copyable nor movable115 // and I don't know how else to insert it into the map116 auto signal = std::make_shared<Signal<E, Args...>>();117 fSignals.insert(std::make_pair(key, signal));118 }119 120 return boost::any_cast<std::shared_ptr<Signal<E, Args...>>>(fSignals.at(key));121}122 123template<typename E, typename... Args>124auto EventManager::Subscribe(const std::string& subscriber,125 std::function<void(typename E::KeyType, Args...)> callback) -> void126{127 const std::type_index event_type_index{typeid(E)};128 const std::type_index callback_type_index{129 typeid(std::function<void(typename E::KeyType, Args...)>)};130 const auto signalsKey = std::make_pair(event_type_index, callback_type_index);131 const auto connectionsKey = std::make_pair(subscriber, signalsKey);132 133 const auto connection = GetSignal<E, Args...>(signalsKey)->connect(callback);134 135 {136 std::lock_guard<std::mutex> lock{fMutex};137 138 if (fConnections.find(connectionsKey) != fConnections.end()) {139 fConnections.at(connectionsKey).disconnect();140 fConnections.erase(connectionsKey);141 }142 fConnections.insert({connectionsKey, connection});143 }144}145 146extern template std::shared_ptr<147 fair::mq::EventManager::Signal<fair::mq::PropertyChangeAsString, std::string>>148 fair::mq::EventManager::GetSignal<fair::mq::PropertyChangeAsString, std::string>(149 const std::pair<std::type_index, std::type_index>& key) const;150 151extern template void152 fair::mq::EventManager::Subscribe<fair::mq::PropertyChangeAsString, std::string>(153 const std::string& subscriber,154 std::function<void(typename fair::mq::PropertyChangeAsString::KeyType, std::string)>);155 156} // namespace fair::mq157 158#endif /* FAIR_MQ_EVENTMANAGER_H */