You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
5.8 KiB
C

/**
*
* \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 <deque>
#include <functional>
#include <limits>
#include <vector>
namespace srsran {
using observer_id = std::size_t;
const std::size_t invalid_observer_id = std::numeric_limits<observer_id>::max();
template <typename... Args>
class observer;
template <typename T>
struct is_observer : public std::false_type {};
template <typename... Args2>
struct is_observer<observer<Args2...> > : public std::true_type {};
//! Type-erasure of Observer
template <typename... Args>
class observer
{
public:
using callback_t = std::function<void(Args...)>;
template <typename Callable>
using enable_if_callback_t =
typename std::enable_if<std::is_convertible<Callable, callback_t>::value and
not is_observer<typename std::decay<Callable>::type>::value>::type;
observer() = default;
//! Subscribe Observer that is a callback
template <typename Callable, typename Check = enable_if_callback_t<Callable> >
observer(Callable&& callable) : callback(std::forward<Callable>(callable))
{}
template <typename Observer,
typename TCheck =
typename std::enable_if<not std::is_convertible<Observer, callback_t>::value, observer_id>::type>
observer(Observer& observer) : callback([&observer](Args... args) { observer.trigger(std::forward<Args>(args)...); })
{}
template <typename Observer>
observer(Observer& observer, void (Observer::*trigger_method)(Args...)) :
callback([&observer, trigger_method](Args... args) { (observer.*trigger_method)(std::forward<Args>(args)...); })
{}
void operator()(Args... args)
{
if (callback) {
callback(std::forward<Args>(args)...);
}
}
explicit operator bool() const { return static_cast<bool>(callback); }
void reset() { callback = nullptr; }
private:
callback_t callback;
};
template <typename... Args>
class base_observable
{
public:
using this_observer_t = observer<Args...>;
//! Subscribe Observer that is a callback
template <typename... Args2>
observer_id subscribe(Args2&&... args)
{
std::size_t id = 0;
for (auto& slot : observers) {
if (not static_cast<bool>(slot)) {
// empty slot found
slot = this_observer_t{std::forward<Args2>(args)...};
return id;
}
id++;
}
// append to end of list
observers.emplace_back(std::forward<Args2>(args)...);
return observers.size() - 1;
}
//! Unsubscribe Observer
bool unsubscribe(observer_id id)
{
if (id < observers.size() and static_cast<bool>(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<bool>(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>(args)...);
}
}
protected:
using observer_list_t = std::deque<this_observer_t>;
~base_observable() = default;
observer_list_t observers;
};
template <typename... Args>
class observable : public base_observable<Args...>
{};
template <typename Event>
using event_observer = observer<const Event&>;
//! Special case of observable for event types
template <typename Event>
class event_dispatcher : public base_observable<const Event&>
{};
//! Event Subject that enqueues events and only signals observers when ::process() is called
template <typename Event>
class event_queue : public base_observable<const Event&>
{
using base_t = base_observable<const Event&>;
public:
template <typename... Args>
void enqueue(Args&&... args)
{
pending_events.emplace_back(std::forward<Args>(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<Event> pending_events;
};
//! RAII class to automatically unsubscribe an observer from an Event
template <typename Event>
class unique_observer_id
{
using subject_t = base_observable<const Event&>;
public:
unique_observer_id(subject_t& parent_, observer_id id_) : parent(&parent_), id(id_) {}
template <typename T>
unique_observer_id(subject_t& parent_, T&& callable) : parent(&parent_)
{
id = parent->subscribe(std::forward<T>(callable));
}
template <typename Observer>
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