diff options
author | Nao Pross <naopross@thearcway.org> | 2019-02-01 21:49:57 +0100 |
---|---|---|
committer | Nao Pross <naopross@thearcway.org> | 2019-02-01 21:49:57 +0100 |
commit | 6d568f45f3db041c8726eca2aa48b0f1bc3c105c (patch) | |
tree | 161242504354db301da31e789f8a6b6b71c8507f | |
parent | dasf (diff) | |
download | flatland-6d568f45f3db041c8726eca2aa48b0f1bc3c105c.tar.gz flatland-6d568f45f3db041c8726eca2aa48b0f1bc3c105c.zip |
Rewrite signal.hpp and signal.cpp to NOT use std::sequence(s)
Its a f*cking mess right now.
-rw-r--r-- | engine/include/core/priority.hpp | 16 | ||||
-rw-r--r-- | engine/include/core/signal.hpp | 367 | ||||
-rw-r--r-- | engine/signal.cpp | 198 | ||||
m--------- | lib/libwsdl2 | 0 |
4 files changed, 141 insertions, 440 deletions
diff --git a/engine/include/core/priority.hpp b/engine/include/core/priority.hpp index ae4a6db..5519327 100644 --- a/engine/include/core/priority.hpp +++ b/engine/include/core/priority.hpp @@ -28,12 +28,24 @@ namespace flat { return lhs.priority < rhs.priority; } - bool operator()(const std::weak_ptr<prioritized> lhs, const std::weak_ptr<prioritized> rhs) { + bool operator()(const prioritized * const lhs, const prioritized * const rhs) { + return lhs->priority < rhs->priority; + } + + bool operator()(const std::unique_ptr<const prioritized> lhs, + const std::unique_ptr<const prioritized> rhs) + { + return operator()(lhs.get(), rhs.get()); + } + + bool operator()(const std::weak_ptr<const prioritized> lhs, + const std::weak_ptr<prioritized> rhs) + { if (auto l = lhs.lock()) { if (auto r = rhs.lock()) { // if both valid, check their priority // in case they are the same, left is prioritized - return l->priority < r->priority; + operator()(l.get(), r.get()); } else { // if right is expired, left is prioritized return true; diff --git a/engine/include/core/signal.hpp b/engine/include/core/signal.hpp index 2ead34a..fd7d575 100644 --- a/engine/include/core/signal.hpp +++ b/engine/include/core/signal.hpp @@ -1,328 +1,173 @@ #pragma once +#include "task.hpp" +#include "types.hpp" +#include "object.hpp" +#include "priority.hpp" +#include "labelled.hpp" +#include "debug.hpp" + #include <map> #include <list> #include <set> #include <initializer_list> -#include "task.hpp" -#include "types.hpp" -#include "object.hpp" #include <functional> #include <memory> #include <cstddef> #include <type_traits> -#include "priority.hpp" -#include "labelled.hpp" - -#include "debug.hpp" // debug header /* * 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<std::size_t> - struct placeholder_template {}; -} - -namespace std +namespace flat::core { - template<size_t N> - struct is_placeholder< helper::placeholder_template<N> > - : integral_constant<size_t, N+1> // the one is important - {}; -} + /* forward decls */ + template<typename F, typename ...Args> + class listener; -namespace flat -{ - //class object; - - namespace core + namespace helper { + /* class helper::signal + * + * is a non constructible, non copyable object used only as abstract + * pointer type. To make anything with it must be first downcasted to a + * core::signal<Args...> + */ + class signal : public prioritized + { + public: + signal(const signal& other) = delete; + signal& operator=(const signal& other) = delete; - /* - * Signal class - */ + protected: + signal(priority_t p) : prioritized(p) {} + }; + } - struct abstract_signal : public labelled, public prioritized + /* class signal<...Args> + * + * is a specialized tuple that contains funnction arguments (...Args) + * that are later used to calll a callback stored in a listener. + */ + template <typename ...Args> + class signal : private std::tuple<Args...>, public helper::signal { - abstract_signal(const std::string& id = "", priority_t prior = priority_t::none); - - // virtual destructor, sure to call right class destructor - virtual ~abstract_signal() {} + private: + /// listener with the same signature is allowed to peek + friend class listener<Args...>; + + public: + signal(Args&&... args, priority_t p = priority_t::none) + : std::tuple<Args...>(std::forward<Args>(args)...), + helper::signal(p) {} }; - template <class ...Args> - struct signal : public abstract_signal - { - - std::tuple<Args...> args; - - signal( Args ... args, - const std::string& id = "", - priority_t prior = priority_t::none) - - : abstract_signal(id, prior), - args(std::forward<Args>(args)...) - { - - } - - /* Alias to flat::core::channel::emit() */ - //bool emit(const std::string& channel) const; - }; - class abstract_listener + namespace helper { - std::list<std::string> filters; - - protected: - - bool check_in_filters(const std::string&) const; - - bool match_filters(const abstract_signal *sig) const; - - public: - - abstract_listener(const std::initializer_list<std::string>& filters = {}); - virtual ~abstract_listener(); + /* class helper::listener + * + * is a non constructible, non copyable object used only as abstract + * pointer type. To make anything with it must be first downcasted to a + * core::listener<Args...> + */ + class listener + { + private: + listener(const signal& other) = delete; + listener& operator=(const listener& other) = delete; - void add_filter(const std::string&); - void remove_filter(const std::string&); + protected: + listener() {} - virtual bool invoke(const abstract_signal *) = 0; - }; + public: + virtual bool invoke(std::shared_ptr<const helper::signal> s) = 0; + }; + } - /* Listener class */ - template <class ...Args> - class listener : public abstract_listener + /* class listener<F, ...Args> + * + * is an object holding a callback, that can be only called by passing + * a signal<...Args> object, which contains the arguments for the callback. + */ + template <typename F, typename ...Args> + class listener : public helper::listener { - public: - using callback = typename std::function<void(Args...)>; using ptr = typename std::shared_ptr<listener<Args...>>; - listener(callback m_callback, const std::initializer_list<std::string>& filters = {}) - : abstract_listener(filters), m_callback(m_callback) - { - - } - - //bool connect(const std::string&); - //bool disconnect(const std::string&); - - template<std::size_t ...Is> - void invoke(const abstract_signal * sig, std::index_sequence<Is...>) - { - const signal<Args...> * pt = dynamic_cast<const signal<Args...>*>(sig); + listener() = delete; + listener(F&& f) : m_callback(std::move(f)) {} - // check if the arguments and the filters match - if (pt && match_filters(sig)) - m_callback(std::get<Is>(pt->args)...); - } - - // implement base class method - virtual bool invoke(const abstract_signal * sig) override - { - return invoke(sig, std::make_index_sequence<sizeof...(Args)>{}); - } + // attempt to call m_callback with s as argument + // m_callback is called only if the signature matches + bool invoke(std::shared_ptr<const helper::signal> s) override; private: - callback m_callback; }; - /* Channel class */ + /* class channel + * + * is an object type through which signals are emitted. + * and is an object type through which listener get their signals. + */ class channel : virtual public labelled, public std::enable_shared_from_this<channel> { - /* Post processing signal stacking */ - queue<std::unique_ptr<abstract_signal>> stack; - - /* Listeners list */ - std::list<std::weak_ptr<abstract_listener>> listeners; - - /* Synchronous task checking for signals */ - task::ptr checker; - - /* Channel mapping */ - static std::map<std::string, std::weak_ptr<channel>> channels; - - bool mapped; - - bool map(); - - bool connect(std::shared_ptr<abstract_listener> l); - - /* - * Connects any bound function to the channel and - * returns the corresponding listener - */ - template<class ...Args> - std::shared_ptr<listener<Args...>> _connect(std::function<void(Args...)> f, - const std::initializer_list<std::string>& filters = {}) - { - std::shared_ptr<listener<Args...>> lis = - std::make_shared<listener<Args...>>(f, filters); - - if (connect(std::static_pointer_cast<abstract_listener>(lis))) - return lis; + private: + // this is a set because sets do not allow identical elements + std::set<std::weak_ptr<helper::listener>> m_listeners; + queue<std::shared_ptr<helper::signal>> m_signals; - return nullptr; - } + /// task to call + std::shared_ptr<task> m_broadcast; public: - using ptr = std::shared_ptr<channel>; - /* - * Constructs a new channel and try to bind it the specified job - * If no job is specified, then flat::main_job will be taken - * - * Note: Remember to check for channel legacy with legit() member function - * A channel is legit if and only if there's no other channel - * corresponding to the same label name - * In general, a channel must be UNIQUE with its label - */ - channel(const std::string& id = ""); - ~channel(); + // TODO: channel() that binds to main_job + channel(job& broadcaster); // do not allow to copy a channel - // it does not have any sense to create a channel with the same name + // it does not make sense to create a channel with the same name channel(const channel&) = delete; channel& operator=(const channel&) = delete; + /// add a signal to the queue/stack of signals (m_signals) template<class ...Args> void emit(const signal<Args...>& sig) { - stack.insert(std::make_unique<abstract_signal>(sig)); - npdebug("Emitted signal: ", sig.label, " ", this); + // insert signal and decay it to a non-template + // unique_ptr<helper::signal> + m_signals.insert(std::make_unique<helper::signal>(sig)); } - - template<class ...Args> - void disconnect(typename listener<Args...>::ptr l) - { - listeners.remove_if( - [l](std::weak_ptr<abstract_listener> p){ - - typename listener<Args...>::ptr pt = p.lock(); - return pt.get() == l.get(); - }); - } + /// for each signal accumulated, call each listener + void broadcast(); - template<class ...Args> - void disconnect(listener<Args...>* l) + /// connect a standalone function + template<typename F, typename ...Args> + std::shared_ptr<listener<F, Args...>> connect(listener<F, Args...>::callback f) { - typename listener<Args...>::ptr pt(l); - disconnect<Args...>(pt); - } - - /* - * Check for legacy - * - * Note: Channels are mapped by their label. - * A channel is legit if and only if there's no other channel - * corresponding to the same label name. - */ - bool legit() const; - - /* - * It tries binding the channel task and make the channel legit - * In case of success, true is returned, otherwise false - */ - bool start(priority_t task_priority = priority_t::none, job * _job = NULL); + auto lis_ptr = std::make_shared<listener<Args...>>(f); + // insert listener and decay it to a non-template + // weak_ptr<helper::listener> + m_listeners.insert(lis_ptr); - /* - * Successfully stops the channel - * Any related job is then detached - */ - void finalize(); - - /* - * Connect a function and returns the - * corresponding listener, placeholder form - */ - template<typename R, class ...Args, std::size_t ...Is> - std::shared_ptr<listener<Args...>> p_connect(R (*mf)(Args...), std::index_sequence<Is...>, const std::initializer_list<std::string>& filters = {}) - { - //using namespace std::placeholders; - //return connect<Args...>(std::bind(mf, obj, _1, _2), filters); - //using namespace helper; - auto b = std::bind(mf, helper::placeholder_template<Is>{}...); - return _connect(b, filters); + return lis_ptr; } - /* - * Connect a function and returns the - * corresponding listener - */ - template<typename R, class ...Args> - std::shared_ptr<listener<Args...>> connect(R (*mf)(Args...), const std::initializer_list<std::string>& filters = {}) + /// connect a member function + template<typename R, typename T, typename F, typename ...Args> + std::shared_ptr<listener<F, Args...>> connect(R T::*mf, T& obj) { - //using namespace std::placeholders; - //return connect<Args...>(std::bind(mf, obj, _1, _2), filters); - //using namespace helper; - return p_connect(mf, std::make_index_sequence<sizeof...(Args)>{}, filters); + return connect([mf](Args&& ...args) -> R { + return (mf(args),...); + }); } - - /* - * Connect a class member function and returns the - * corresponding listener, placeholder form - */ - template<typename R, typename T, class ...Args, size_t ...Is> - std::shared_ptr<listener<Args...>> c_connect(R (T::*mf)(Args...), T* obj, std::index_sequence<Is...>, const std::initializer_list<std::string>& filters = {}) - { - //using namespace std::placeholders; - //return connect<Args...>(std::bind(mf, obj, _1, _2), filters); - //using namespace helper; - auto b = std::bind(mf, obj, helper::placeholder_template<Is>{}...); - return _connect(b, filters); - } - - /* - * Connect a class member function and returns the - * corresponding listener - */ - template<typename R, typename T, class ...Args> - std::shared_ptr<listener<Args...>> connect(R (T::*mf)(Args...), T* obj, const std::initializer_list<std::string>& filters = {}) - { - //using namespace std::placeholders; - //return connect<Args...>(std::bind(mf, obj, _1, _2), filters); - //using namespace helper; - return c_connect(mf, obj, std::make_index_sequence<sizeof...(Args)>{}, filters); - } - - /* - * Find channel by its name - */ - static ptr find(const std::string&); - - /* - * Safer than constructor - * It constructs a new channel checking whether it is legit or not. - * It returns a nullptr if the legacy is not verified. - * - * Note: Channels are mapped by their label. - * A channel is legit if and only if there's no other channel - * corresponding to the same label name. - */ - static ptr create(const std::string& id, priority_t prior = priority_t::none, job * _job = NULL); - - void check_and_call(); }; - - - } } diff --git a/engine/signal.cpp b/engine/signal.cpp index 941d641..362fbdf 100644 --- a/engine/signal.cpp +++ b/engine/signal.cpp @@ -7,198 +7,42 @@ using namespace flat::core; -std::map<std::string, std::weak_ptr<channel>> channel::channels; +/* listener */ -channel::channel(const std::string& id) : labelled(id), mapped(false) +template<typename F, typename ...Args> +bool listener<F, Args ...>::invoke(std::shared_ptr<const helper::signal> s) { -} - -channel::~channel() -{ - // by default it should be there - if (mapped) - channels.erase(label); -} - -bool channel::start(priority_t prior, job * _job) -{ - npdebug("Trying to map channel: ", label); - - if (map()) - checker.reset(); // force count to zero, it should remove task too - else - return false; - - npdebug("Starting channel: ", label); + const signal<Args...> *p = dynamic_cast<const signal<Args...> *>(s.get()); - // take main job if no job is specified - if (!_job) - _job = &flat::main_job(); + if (p != nullptr) + std::apply(m_callback, *p); - // Initialize task - checker = _job->delegate_task(&channel::check_and_call, this, prior); - - return true; + // return true if p was called + return (p != nullptr); } -void channel::finalize() -{ - npdebug("Finalizing channel: ", label) - - if (mapped) { - channels.erase(label); - checker.reset(); - } -} +/* channel */ -bool channel::map() +channel::channel(job& broadcaster) { - if (!mapped) { - - channel::ptr other = channel::find(label); - - if (!other) { - - channels.insert(std::pair<std::string, std::weak_ptr<channel>>(label, weak_from_this())); - mapped = true; - } - } - - return mapped; + m_broadcast = broadcaster.delegate_task(&channel::broadcast, this); } -bool channel::connect(std::shared_ptr<abstract_listener> l) +void channel::broadcast() { - /* Control not to double */ - for (auto lis : listeners) - { - auto pt = lis.lock(); - - if (pt.get() == l.get()) - return false; - } - - listeners.push_back(l); - return true; -} - -bool channel::legit() const -{ - return mapped; -} - -channel::ptr channel::create(const std::string& id, priority_t prior, job * _job) -{ - ptr out = std::make_shared<channel>(id); - - // do not create a non-legit channel - return out->start(prior, _job) ? out : nullptr; -} - -channel::ptr channel::find(const std::string& id) -{ - if (id.empty()) - return nullptr; - - auto it = channels.find(id); - - return (it == channels.end()) ? nullptr : (*it).second.lock(); -} - -void channel::check_and_call() -{ - if (!stack.empty()) { - - npdebug("Signal detected: ", label, " ", this) - - std::vector<std::weak_ptr<abstract_listener>> to_erase; + std::vector<std::weak_ptr<helper::listener>> to_erase; - // TODO, maybe it exists pop - /* for each listener_s, catch signal */ + for (const auto& sig : m_signals) { + for (auto l : m_listeners) { + // check that is not expired + std::shared_ptr<helper::listener> pt; - for (auto l : listeners) - { - std::shared_ptr<abstract_listener> pt; - - if (pt = l.lock()) - { - for (const auto& sig : stack) - { - // pass simple pointer - pt->invoke(sig.get()); - } - } - else + if ((pt = l.lock())) { + pt->invoke(sig); + } else { to_erase.push_back(l); + } } - - /* Erase invalidated listeners */ - listeners.remove_if( - [](std::weak_ptr<abstract_listener> e) { return e.expired(); }); - - stack.clear(); // TODO not so efficient - } -} - - -/* signal class */ - -abstract_signal::abstract_signal(const std::string& id, priority_t priority) - - : labelled(id, true), - prioritized(priority) {} - -/*bool signal::emit(const std::string& chan) const -{ - channel::ptr c = channel::find(chan); - - if (!c) - return false; - - c->emit(*this); - - return true; -}*/ - - -/* listener_s class */ - -abstract_listener::abstract_listener(const std::initializer_list<std::string>& filters) - - : filters(filters) -{ - -} - -abstract_listener::~abstract_listener() -{ -} - -bool abstract_listener::check_in_filters(const std::string& filter) const -{ - for (const auto& f : filters) - { - if (filter == f) - return true; } - - return false; -} - -void abstract_listener::add_filter(const std::string& f) -{ - filters.push_back(f); -} - -void abstract_listener::remove_filter(const std::string& f) -{ - filters.remove(f); } - -bool abstract_listener::match_filters(const abstract_signal *sig) const -{ - return (!sig->label.empty() && check_in_filters(sig->label)) || - (sig->label.empty() && filters.empty()); -} - diff --git a/lib/libwsdl2 b/lib/libwsdl2 -Subproject 736ec71fce673d5aa88228b96acfe6c6862a223 +Subproject 71b593ebd1a015a4cb9bfe0eebafc78d897ff28 |