/** * * \section COPYRIGHT * * Copyright 2013-2021 Software Radio Systems Limited * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the distribution. * */ #ifndef SRSRAN_OBSERVER_H #define SRSRAN_OBSERVER_H #include #include #include #include namespace srsran { using observer_id = std::size_t; const std::size_t invalid_observer_id = std::numeric_limits::max(); template class observer; template struct is_observer : public std::false_type {}; template struct is_observer > : public std::true_type {}; //! Type-erasure of Observer template class observer { public: using callback_t = std::function; template using enable_if_callback_t = typename std::enable_if::value and not is_observer::type>::value>::type; observer() = default; //! Subscribe Observer that is a callback template > observer(Callable&& callable) : callback(std::forward(callable)) {} template ::value, observer_id>::type> observer(Observer& observer) : callback([&observer](Args... args) { observer.trigger(std::forward(args)...); }) {} template observer(Observer& observer, void (Observer::*trigger_method)(Args...)) : callback([&observer, trigger_method](Args... args) { (observer.*trigger_method)(std::forward(args)...); }) {} void operator()(Args... args) { if (callback) { callback(std::forward(args)...); } } explicit operator bool() const { return static_cast(callback); } void reset() { callback = nullptr; } private: callback_t callback; }; template class base_observable { public: using this_observer_t = observer; //! Subscribe Observer that is a callback template observer_id subscribe(Args2&&... args) { std::size_t id = 0; for (auto& slot : observers) { if (not static_cast(slot)) { // empty slot found slot = this_observer_t{std::forward(args)...}; return id; } id++; } // append to end of list observers.emplace_back(std::forward(args)...); return observers.size() - 1; } //! Unsubscribe Observer bool unsubscribe(observer_id id) { if (id < observers.size() and static_cast(observers[id])) { observers[id] = nullptr; return true; } return false; } std::size_t nof_observers() const { std::size_t count = 0; for (auto& slot : observers) { count += static_cast(slot) ? 1 : 0; } return count; } void unsubscribe_all() { observers.clear(); } //! Signal result to observers void dispatch(Args... args) { for (auto& obs_callback : observers) { obs_callback(std::forward(args)...); } } protected: using observer_list_t = std::deque; ~base_observable() = default; observer_list_t observers; }; template class observable : public base_observable {}; template using event_observer = observer; //! Special case of observable for event types template class event_dispatcher : public base_observable {}; //! Event Subject that enqueues events and only signals observers when ::process() is called template class event_queue : public base_observable { using base_t = base_observable; public: template void enqueue(Args&&... args) { pending_events.emplace_back(std::forward(args)...); } void process() { for (auto& ev : pending_events) { base_t::dispatch(ev); } pending_events.clear(); } private: // forbid direct dispatches using base_t::dispatch; std::vector pending_events; }; //! RAII class to automatically unsubscribe an observer from an Event template class unique_observer_id { using subject_t = base_observable; public: unique_observer_id(subject_t& parent_, observer_id id_) : parent(&parent_), id(id_) {} template unique_observer_id(subject_t& parent_, T&& callable) : parent(&parent_) { id = parent->subscribe(std::forward(callable)); } template unique_observer_id(subject_t& parent_, Observer& observer, void (Observer::*trigger_method)(const Event&)) : parent(&parent_) { id = parent->subscribe(observer, trigger_method); } unique_observer_id(unique_observer_id&& other) noexcept : parent(other.parent), id(other.id) { other.parent = nullptr; } unique_observer_id(const unique_observer_id& other) = delete; unique_observer_id& operator=(unique_observer_id&& other) noexcept { parent = other.parent; id = other.id; other.id = invalid_observer_id; return *this; } unique_observer_id& operator=(const unique_observer_id& other) = delete; ~unique_observer_id() { if (id != invalid_observer_id) { parent->unsubscribe(id); } } observer_id get_id() const { return id; } bool is_valid() const { return id != invalid_observer_id; } observer_id release() { observer_id ret = id; id = invalid_observer_id; return ret; } private: subject_t* parent; observer_id id; }; } // namespace srsran #endif // SRSRAN_OBSERVER_H