Skip to content

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 */