mirror of https://github.com/pvnis/srsRAN_4G.git
observer pattern classes. Allows auto and explicit dispatch, and specifying which trigger method to use of an observer.
parent
997552ee45
commit
b2313e3631
@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* This file is part of srsLTE.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_OBSERVER_H
|
||||||
|
#define SRSLTE_OBSERVER_H
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace srslte {
|
||||||
|
|
||||||
|
using observer_id = std::size_t;
|
||||||
|
const size_t invalid_observer_id = std::numeric_limits<observer_id>::max();
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
class base_observable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using callback_t = std::function<void(Args...)>;
|
||||||
|
|
||||||
|
//! Subscribe Observer that is a callback
|
||||||
|
template <typename Callable>
|
||||||
|
typename std::enable_if<std::is_convertible<Callable, callback_t>::value, observer_id>::type
|
||||||
|
subscribe(Callable&& callable)
|
||||||
|
{
|
||||||
|
return subscribe_common(callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Subscribe Observer type with method Observer::trigger(Args...)
|
||||||
|
template <typename Observer>
|
||||||
|
typename std::enable_if<not std::is_convertible<Observer, callback_t>::value, observer_id>::type
|
||||||
|
subscribe(Observer& observer)
|
||||||
|
{
|
||||||
|
return subscribe_common([&observer](Args... args) { observer.trigger(std::forward<Args>(args)...); });
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Subscribe Observer type with custom trigger method
|
||||||
|
template <typename Observer>
|
||||||
|
observer_id subscribe(Observer& observer, void (Observer::*trigger_method)(Args...))
|
||||||
|
{
|
||||||
|
return subscribe_common(
|
||||||
|
[&observer, trigger_method](Args... args) { (observer.*trigger_method)(std::forward<Args>(args)...); });
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Unsubscribe Observer
|
||||||
|
bool unsubscribe(observer_id id)
|
||||||
|
{
|
||||||
|
if (id < observers.size() and static_cast<bool>(observers[id])) {
|
||||||
|
observers[id] = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nof_observers() const
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
for (auto& slot : observers) {
|
||||||
|
count += static_cast<bool>(slot) ? 1 : 0;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Signal result to observers
|
||||||
|
void dispatch(Args... args)
|
||||||
|
{
|
||||||
|
for (auto& obs_callback : observers) {
|
||||||
|
if (obs_callback) {
|
||||||
|
obs_callback(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using observer_list_t = std::deque<callback_t>;
|
||||||
|
|
||||||
|
~base_observable() = default;
|
||||||
|
|
||||||
|
template <typename Callable>
|
||||||
|
observer_id subscribe_common(Callable&& callable)
|
||||||
|
{
|
||||||
|
size_t id = 0;
|
||||||
|
for (auto& slot : observers) {
|
||||||
|
if (not static_cast<bool>(slot)) {
|
||||||
|
// empty slot found
|
||||||
|
slot = std::forward<Callable>(callable);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
// append to end of list
|
||||||
|
observers.emplace_back(std::forward<Callable>(callable));
|
||||||
|
return observers.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer_list_t observers;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
class observable : public base_observable<Args...>
|
||||||
|
{};
|
||||||
|
|
||||||
|
//! 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 srslte
|
||||||
|
|
||||||
|
#endif // SRSLTE_OBSERVER_H
|
@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* This file is part of srsLTE.
|
||||||
|
*
|
||||||
|
* srsLTE is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* srsLTE is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* A copy of the GNU Affero General Public License can be found in
|
||||||
|
* the LICENSE file in the top-level directory of this distribution
|
||||||
|
* and at http://www.gnu.org/licenses/.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srslte/adt/observer.h"
|
||||||
|
#include "srslte/common/test_common.h"
|
||||||
|
|
||||||
|
struct M {
|
||||||
|
M() = default;
|
||||||
|
explicit M(int v) : val(v) {}
|
||||||
|
M(M&&) noexcept = default;
|
||||||
|
M(const M&) = delete;
|
||||||
|
M& operator=(M&&) noexcept = default;
|
||||||
|
M& operator=(const M&) = delete;
|
||||||
|
|
||||||
|
M& operator+=(int i)
|
||||||
|
{
|
||||||
|
val += i;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lval_observer_tester {
|
||||||
|
void trigger(M v_) { v = std::move(v_); }
|
||||||
|
void foo(M v_)
|
||||||
|
{
|
||||||
|
v = std::move(v_);
|
||||||
|
v += 1;
|
||||||
|
}
|
||||||
|
M v;
|
||||||
|
};
|
||||||
|
struct cref_observer_tester {
|
||||||
|
void trigger(const M& v_) { v.val = v_.val; }
|
||||||
|
void foo(const M& v_) { v.val = v_.val + 1; }
|
||||||
|
M v;
|
||||||
|
};
|
||||||
|
struct lref_observer_tester {
|
||||||
|
void trigger(M& v_) { v.val = v_.val; }
|
||||||
|
void foo(M& v_) { v.val = v_.val + 1; }
|
||||||
|
M v;
|
||||||
|
};
|
||||||
|
struct rref_observer_tester {
|
||||||
|
void trigger(M&& v_) { v = std::move(v_); }
|
||||||
|
void foo(M&& v_)
|
||||||
|
{
|
||||||
|
v = std::move(v_);
|
||||||
|
v += 1;
|
||||||
|
}
|
||||||
|
M v;
|
||||||
|
};
|
||||||
|
|
||||||
|
int observable_test()
|
||||||
|
{
|
||||||
|
// TEST l-value arguments passed by value
|
||||||
|
{
|
||||||
|
M val;
|
||||||
|
srslte::observable<M> subject;
|
||||||
|
TESTASSERT(subject.nof_observers() == 0);
|
||||||
|
|
||||||
|
srslte::observer_id id1 = subject.subscribe([&val](M v) { val = std::move(v); });
|
||||||
|
|
||||||
|
lval_observer_tester observer{}, observer2{};
|
||||||
|
srslte::observer_id id2 = subject.subscribe(observer);
|
||||||
|
srslte::observer_id id3 = subject.subscribe(observer2, &lval_observer_tester::foo);
|
||||||
|
|
||||||
|
TESTASSERT(subject.nof_observers() == 3);
|
||||||
|
TESTASSERT(val.val == 0);
|
||||||
|
subject.dispatch(M{5});
|
||||||
|
TESTASSERT(val.val == 5);
|
||||||
|
TESTASSERT(observer.v.val == 5);
|
||||||
|
TESTASSERT(observer2.v.val == 6);
|
||||||
|
|
||||||
|
subject.unsubscribe(id1);
|
||||||
|
TESTASSERT(subject.nof_observers() == 2);
|
||||||
|
subject.unsubscribe(id2);
|
||||||
|
TESTASSERT(subject.nof_observers() == 1);
|
||||||
|
subject.unsubscribe(id3);
|
||||||
|
TESTASSERT(subject.nof_observers() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test l-value arguments passed by const ref
|
||||||
|
{
|
||||||
|
M val;
|
||||||
|
srslte::observable<const M&> subject;
|
||||||
|
TESTASSERT(subject.nof_observers() == 0);
|
||||||
|
|
||||||
|
subject.subscribe([&val](const M& v) { val.val = v.val; });
|
||||||
|
|
||||||
|
cref_observer_tester observer{}, observer2{};
|
||||||
|
subject.subscribe(observer);
|
||||||
|
subject.subscribe(observer2, &cref_observer_tester::foo);
|
||||||
|
|
||||||
|
M new_val{6};
|
||||||
|
subject.dispatch(new_val);
|
||||||
|
TESTASSERT(val.val == 6);
|
||||||
|
TESTASSERT(observer.v.val == 6);
|
||||||
|
TESTASSERT(observer2.v.val == 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test l-value arguments passed by ref
|
||||||
|
{
|
||||||
|
M val;
|
||||||
|
srslte::observable<M&> subject;
|
||||||
|
TESTASSERT(subject.nof_observers() == 0);
|
||||||
|
|
||||||
|
subject.subscribe([&val](M& v) { val = std::move(v); });
|
||||||
|
|
||||||
|
lref_observer_tester observer{}, observer2{};
|
||||||
|
subject.subscribe(observer);
|
||||||
|
subject.subscribe(observer2, &lref_observer_tester::foo);
|
||||||
|
|
||||||
|
M new_val{6};
|
||||||
|
subject.dispatch(new_val);
|
||||||
|
TESTASSERT(val.val == 6);
|
||||||
|
TESTASSERT(observer.v.val == 6);
|
||||||
|
TESTASSERT(observer2.v.val == 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test r-value arguments
|
||||||
|
{
|
||||||
|
M val;
|
||||||
|
srslte::observable<M&&> subject;
|
||||||
|
TESTASSERT(subject.nof_observers() == 0);
|
||||||
|
|
||||||
|
srslte::observer_id id1 = subject.subscribe([&val](M&& v) { val = std::move(v); });
|
||||||
|
|
||||||
|
rref_observer_tester observer{}, observer2{};
|
||||||
|
srslte::observer_id id2 = subject.subscribe(observer);
|
||||||
|
srslte::observer_id id3 = subject.subscribe(observer2, &rref_observer_tester::foo);
|
||||||
|
|
||||||
|
subject.dispatch(M{3});
|
||||||
|
TESTASSERT(val.val == 3);
|
||||||
|
TESTASSERT(observer.v.val == 3);
|
||||||
|
TESTASSERT(observer2.v.val == 4);
|
||||||
|
|
||||||
|
subject.unsubscribe(id1);
|
||||||
|
subject.unsubscribe(id2);
|
||||||
|
subject.unsubscribe(id3);
|
||||||
|
TESTASSERT(subject.nof_observers() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_dispatcher_test()
|
||||||
|
{
|
||||||
|
srslte::event_dispatcher<M> signaller;
|
||||||
|
|
||||||
|
M val;
|
||||||
|
signaller.subscribe([&val](const M& ev) { val.val = ev.val; });
|
||||||
|
|
||||||
|
cref_observer_tester observer, observer2;
|
||||||
|
signaller.subscribe(observer);
|
||||||
|
signaller.subscribe(observer2, &cref_observer_tester::foo);
|
||||||
|
|
||||||
|
TESTASSERT(val.val == 0);
|
||||||
|
TESTASSERT(observer.v.val == 0);
|
||||||
|
TESTASSERT(observer2.v.val == 0);
|
||||||
|
signaller.dispatch(M{2});
|
||||||
|
TESTASSERT(val.val == 2);
|
||||||
|
TESTASSERT(observer.v.val == 2);
|
||||||
|
TESTASSERT(observer2.v.val == 3);
|
||||||
|
|
||||||
|
val.val = 1;
|
||||||
|
observer.v.val = 0;
|
||||||
|
observer2.v.val = 5;
|
||||||
|
signaller.dispatch(M{2});
|
||||||
|
TESTASSERT(val.val == 2);
|
||||||
|
TESTASSERT(observer.v.val == 2);
|
||||||
|
TESTASSERT(observer2.v.val == 3);
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_queue_test()
|
||||||
|
{
|
||||||
|
srslte::event_queue<M> signaller;
|
||||||
|
|
||||||
|
M val;
|
||||||
|
signaller.subscribe([&val](const M& ev) { val.val = ev.val; });
|
||||||
|
cref_observer_tester observer, observer2;
|
||||||
|
signaller.subscribe(observer);
|
||||||
|
signaller.subscribe(observer2, &cref_observer_tester::foo);
|
||||||
|
|
||||||
|
TESTASSERT(val.val == 0);
|
||||||
|
TESTASSERT(observer.v.val == 0);
|
||||||
|
TESTASSERT(observer2.v.val == 0);
|
||||||
|
signaller.enqueue(M{2});
|
||||||
|
TESTASSERT(val.val == 0);
|
||||||
|
TESTASSERT(observer.v.val == 0);
|
||||||
|
TESTASSERT(observer2.v.val == 0);
|
||||||
|
signaller.process();
|
||||||
|
TESTASSERT(val.val == 2);
|
||||||
|
TESTASSERT(observer.v.val == 2);
|
||||||
|
TESTASSERT(observer2.v.val == 3);
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unique_subscribe_test()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
srslte::event_dispatcher<M> signaller;
|
||||||
|
cref_observer_tester observer;
|
||||||
|
TESTASSERT(signaller.nof_observers() == 0);
|
||||||
|
{
|
||||||
|
srslte::unique_observer_id<M> obs{signaller, observer};
|
||||||
|
TESTASSERT(signaller.nof_observers() == 1);
|
||||||
|
}
|
||||||
|
TESTASSERT(signaller.nof_observers() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
srslte::event_queue<M> signaller;
|
||||||
|
cref_observer_tester observer;
|
||||||
|
TESTASSERT(signaller.nof_observers() == 0);
|
||||||
|
{
|
||||||
|
srslte::unique_observer_id<M> obs{signaller, observer};
|
||||||
|
TESTASSERT(signaller.nof_observers() == 1);
|
||||||
|
}
|
||||||
|
TESTASSERT(signaller.nof_observers() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SRSLTE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TESTASSERT(observable_test() == SRSLTE_SUCCESS);
|
||||||
|
TESTASSERT(event_dispatcher_test() == SRSLTE_SUCCESS);
|
||||||
|
TESTASSERT(event_queue_test() == SRSLTE_SUCCESS);
|
||||||
|
TESTASSERT(unique_subscribe_test() == SRSLTE_SUCCESS);
|
||||||
|
|
||||||
|
printf("Success\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue