diff options
author | ancarola <raffaele.ancarola@epfl.ch> | 2019-01-28 23:32:05 +0100 |
---|---|---|
committer | ancarola <raffaele.ancarola@epfl.ch> | 2019-01-28 23:32:05 +0100 |
commit | 62d4db15ed7ff5fd0a1fbd019542f038e6891f7b (patch) | |
tree | 5157b7a480d4be94022481a2acea5a979c35a93f | |
parent | Listener pre-reconstruction (diff) | |
download | flatland-62d4db15ed7ff5fd0a1fbd019542f038e6891f7b.tar.gz flatland-62d4db15ed7ff5fd0a1fbd019542f038e6891f7b.zip |
Experimental status of signal
-rw-r--r-- | engine/include/core/signal.hpp | 203 | ||||
-rw-r--r-- | engine/signal.cpp | 102 |
2 files changed, 170 insertions, 135 deletions
diff --git a/engine/include/core/signal.hpp b/engine/include/core/signal.hpp index 4a4345c..fbcd043 100644 --- a/engine/include/core/signal.hpp +++ b/engine/include/core/signal.hpp @@ -9,9 +9,37 @@ #include "object.hpp" #include <functional> #include <memory> +#include <cstddef> +#include <type_traits> #include "priority.hpp" #include "labelled.hpp" +/* + * Some tools for variadic binding templates + */ + +namespace helper +{ + + template<int...> struct int_sequence {}; + + template<int N, int... Is> struct make_int_sequence + : make_int_sequence<N-1, N-1, Is...> {}; + template<int... Is> struct make_int_sequence<0, Is...> + : int_sequence<Is...> {}; + + template<int> + struct placeholder_template {}; +} + +namespace std +{ + template<int N> + struct is_placeholder< helper::placeholder_template<N> > + : integral_constant<int, N+1> // the one is important + {}; +} + namespace flat { //class object; @@ -19,80 +47,106 @@ namespace flat namespace core { - class signal : virtual public labelled, virtual public prioritized + /* + * Signal class + */ + + struct abstract_signal : public labelled, public prioritized + { + abstract_signal(const std::string& id = "", priority_t prior = priority_t::none); + + // virtual destructor, sure to call right class destructor + virtual ~abstract_signal() {} + }; + + template <class ...Args> + struct signal : public abstract_signal { - public: + std::tuple<Args...> args; + + signal( Args&& ... args, + const std::string& id = "", + priority_t prior = priority_t::none) - struct package + : abstract_signal(id, prior), args(std::forward<Args>(args)...) { - package(void *data) : data(data) {} - template<class T> - T * get() { + } - return reinterpret_cast<T*>(data); - } + /* Alias to flat::core::channel::emit() */ + //bool emit(const std::string& channel) const; + }; - void * data; - }; - - object * sender; - package m_package; + class abstract_listener + { + std::list<std::string> filters; + + protected: - signal( object * sender, - const std::string& id = "", - void * data = 0, - priority_t prior = priority_t::none); + bool check_in_filters(const std::string&) const; - /* Alias to flat::core::channel::emit() */ - bool emit(const std::string& channel) const; + bool match_filters(const abstract_signal *sig) const; + + public: + + abstract_listener(const std::initializer_list<std::string>& filters = {}); + virtual ~abstract_listener(); + + void add_filter(const std::string&); + void remove_filter(const std::string&); + + virtual void invoke(const abstract_signal&) = 0; }; /* Listener class */ - class listener + template <class ...Args> + class listener : public abstract_listener { public: - using callback = std::function<void(const object*, signal::package)>; - using ptr = std::shared_ptr<listener>; + using callback = std::function<void(Args...)>; + using ptr = std::shared_ptr<listener<Args...>>; - listener(callback m_callback, const std::initializer_list<std::string>& filters = {}); - ~listener(); + listener(callback m_callback, const std::initializer_list<std::string>& filters = {}) + : abstract_listener(filters), m_callback(m_callback) + { - void add_filter(const std::string&); - void remove_filter(const std::string&); + } - bool connect(const std::string&); - bool disconnect(const std::string&); + //bool connect(const std::string&); + //bool disconnect(const std::string&); - void invoke(const signal&); + template<int ...Is> + void invoke(abstract_signal *sig, helper::int_sequence<Is...>) + { + signal<Args...>* pt = dynamic_cast<signal<Args...>*>(sig); + + // check if the arguments and the filters match + if (pt && match_filters(sig)) + m_callback(std::get<Is>(pt->args)...); + } - /* Allow to safely bind a functor */ - template<typename R, typename T> - static ptr create( R T::*mf, - T& obj, - const std::initializer_list<std::string>& filters = {}) { - return std::make_shared<listener>(std::bind(mf, obj), filters); + // implement base class method + virtual bool invoke(abstract_signal* sig) override + { + return invoke(sig, helper::make_int_sequence<sizeof...(Args)>{}); } private: callback m_callback; - - std::list<std::string> filters; - - bool check_in_filters(const std::string&) const; }; + /* Channel class */ class channel : virtual public labelled, public std::enable_shared_from_this<channel> { /* Post processing signal stacking */ - queue<signal> stack; + queue<std::unique_ptr<abstract_signal>> stack; /* Listeners list */ - std::list<std::weak_ptr<listener>> listeners; + std::list<std::weak_ptr<abstract_listener>> listeners; /* Synchronous task checking for signals */ task::ptr checker; @@ -103,7 +157,9 @@ namespace flat bool mapped; bool map(); - + + bool connect(std::shared_ptr<abstract_listener> l); + public: using ptr = std::shared_ptr<channel>; @@ -124,14 +180,32 @@ namespace flat // it does not have any sense to create a channel with the same name channel(const channel&) = delete; channel& operator=(const channel&) = delete; - - void emit(const signal&); + + template<class ...Args> + void emit(const signal<Args...>& sig) + { + stack.insert(std::make_unique<abstract_signal>(sig)); + npdebug("Emitted signal: ", sig.label, " ", this); + } + - bool connect(listener::ptr l); - void disconnect(listener::ptr l); + template<class ...Args> + void disconnect(listener<Args...>::ptr l) + { + listeners.remove_if( + [l](std::weak_ptr<listener> p){ + + listener::ptr pt = p.lock(); + return pt.get() == l.get(); + }); + } - bool connect(listener* l); - void disconnect(listener* l); + template<class ...Args> + void disconnect(listener<Args...>* l) + { + listener<Args...>::ptr pt(l); + disconnect<Args...>(pt); + } /* * Check for legacy @@ -148,17 +222,40 @@ namespace flat */ bool start(priority_t task_priority = priority_t::none, job * _job = NULL); + /* + * Successfully stops the channel + * Any related job is then detached + */ void finalize(); - listener::ptr connect(listener::callback f, - const std::initializer_list<std::string>& filters = {}); + /* + * Connects any bound function to the channel and + * returns the corresponding listener + */ + template<class ...Args> + listener<Args...>::ptr connect( listener<Args...>::callback f, + const std::initializer_list<std::string>& filters = {}) + { + listener<Args...>::ptr lis = std::make_shared<listener<Args..>>(f, filters); + + if (connect(static_pointer_cast<abstract_listener>(lis))) + return lis; - template<typename R, typename T> + return nullptr; + } + + /* + * Connect a class member function and returns the + * corresponding listener + */ + template<typename R, typename T, class Args...> inline listener::ptr connect(R T::*mf, T* obj, const std::initializer_list<std::string>& filters = {}) { - using namespace std::placeholders; - return connect(std::bind(mf, obj, _1, _2), filters); + //using namespace std::placeholders; + //return connect<Args...>(std::bind(mf, obj, _1, _2), filters); + using namespace helper; + return connect<Args...>(std::bind(mf, obj, make_int_sequence<sizeof...(Args)>{}), filters); } /* diff --git a/engine/signal.cpp b/engine/signal.cpp index c5b5ad4..675bdc8 100644 --- a/engine/signal.cpp +++ b/engine/signal.cpp @@ -68,13 +68,7 @@ bool channel::map() return mapped; } -void channel::emit(const signal& sig) -{ - stack.insert(sig); - npdebug("Emitted signal: ", sig.label, " ", this); -} - -bool channel::connect(listener::ptr l) +bool channel::connect(std::shared_ptr<abstract_listener> l) { /* Control not to double */ for (auto lis : listeners) @@ -89,38 +83,6 @@ bool channel::connect(listener::ptr l) return true; } -void channel::disconnect(listener::ptr l) -{ - listeners.remove_if( - [l](std::weak_ptr<listener> p){ - - listener::ptr pt = p.lock(); - return pt.get() == l.get(); - }); -} - -bool channel::connect(listener* l) -{ - listener::ptr pt(l); - return connect(pt); -} - -void channel::disconnect(listener* l) -{ - listener::ptr pt(l); - disconnect(pt); -} - -listener::ptr channel::connect(listener::callback f, const std::initializer_list<std::string>& filters) -{ - listener::ptr lis = std::make_shared<listener>(f, filters); - - if (connect(lis)) - return lis; - - return nullptr; -} - bool channel::legit() const { return mapped; @@ -150,19 +112,22 @@ void channel::check_and_call() npdebug("Signal detected: ", label, " ", this) - std::vector<std::weak_ptr<listener>> to_erase; + std::vector<std::weak_ptr<abstract_listener>> to_erase; // TODO, maybe it exists pop /* for each listener_s, catch signal */ for (auto l : listeners) { - listener::ptr pt; + std::shared_ptr<abstract_listener> pt; if (pt = l.lock()) { for (const auto& sig : stack) - pt->invoke(sig); + { + // pass simple pointer + pt->invoke(sig.get()); + } } else to_erase.push_back(l); @@ -170,7 +135,7 @@ void channel::check_and_call() /* Erase invalidated listeners */ listeners.remove_if( - [](std::weak_ptr<listener> e) { return e.expired(); }); + [](std::weak_ptr<abstract_listener> e) { return e.expired(); }); stack.clear(); // TODO not so efficient } @@ -179,46 +144,40 @@ void channel::check_and_call() /* signal class */ -signal::signal( object *sender, - const std::string& id, - void *data, - priority_t priority) +abstract_signal::abstract_signal(const std::string& id, priority_t priority) : labelled(id, true), prioritized(priority), - sender(sender), - m_package(package(data)) { } -bool signal::emit(const std::string& chan) const +/*bool signal::emit(const std::string& chan) const { channel::ptr c = channel::find(chan); if (!c) return false; - /* Finally emit in channel */ c->emit(*this); return true; -} +}*/ /* listener_s class */ -listener::listener(callback m_callback, const std::initializer_list<std::string>& filters) +abstract_listener::abstract_listener(const std::initializer_list<std::string>& filters) - : m_callback(m_callback), filters(filters) + : filters(filters) { } -listener::~listener() +abstract_listener::~abstract_listener() { } -bool listener::check_in_filters(const std::string& filter) const +bool abstract_listener::check_in_filters(const std::string& filter) const { for (const auto& f : filters) { @@ -229,40 +188,19 @@ bool listener::check_in_filters(const std::string& filter) const return false; } -void listener::add_filter(const std::string& f) +void abstract_listener::add_filter(const std::string& f) { filters.push_back(f); } -void listener::remove_filter(const std::string& f) +void abstract_listener::remove_filter(const std::string& f) { filters.remove(f); } -bool listener::connect(const std::string& chan) -{ - channel::ptr c = channel::find(chan); - - if (!c) - c->connect(this); - - return bool(c); -} - -bool listener::disconnect(const std::string& chan) -{ - channel::ptr c = channel::find(chan); - - if (!c) - c->disconnect(this); - - return bool(c); -} - -void listener::invoke(const signal& sig) +bool abstract_listener::match_filters(const abstract_signal *sig) const { - if ( (!sig.label.empty() && check_in_filters(sig.label)) || - (sig.label.empty() && filters.empty())) - m_callback(sig.sender, sig.m_package); + return (!sig.label.empty() && check_in_filters(sig.label)) || + (sig.label.empty() && filters.empty()); } |