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 /engine/include/core/signal.hpp | |
parent | Listener pre-reconstruction (diff) | |
download | flatland-62d4db15ed7ff5fd0a1fbd019542f038e6891f7b.tar.gz flatland-62d4db15ed7ff5fd0a1fbd019542f038e6891f7b.zip |
Experimental status of signal
Diffstat (limited to 'engine/include/core/signal.hpp')
-rw-r--r-- | engine/include/core/signal.hpp | 203 |
1 files changed, 150 insertions, 53 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); } /* |