#pragma once #include "function.h" #include "optional.h" #include "owning_list.h" namespace panda { namespace { template struct optional_type { using type = optional; static type default_value() { return type{}; } }; template <> struct optional_type { static void default_value(){} using type = void; }; } template class CallbackDispatcher { public: struct Event; using OptionalRet = typename optional_type::type; using Callback = function; using SimpleCallback = function; struct Wrapper { Callback real; SimpleCallback simple; explicit Wrapper (Callback real) : real(real) {} explicit Wrapper (SimpleCallback simple) : simple(simple) {} template auto operator() (Event& e, RealArgs&&... args) -> decltype(real(e, args...)) { if (real) return real(e, std::forward(args)...); simple(args...); return e.next(std::forward(args)...); } bool equal (const Wrapper& oth) { if (simple) return simple == oth.simple; return real == oth.real; } template decltype(simple == std::declval()) equal (const T& oth) { return simple && simple == oth; } template decltype(real == std::declval()) equal (const T& oth) { return real && real == oth; } }; template using add_const_ref_t = typename std::conditional::value, T, const T&>::type; using CallbackList = owning_list; struct Event { CallbackDispatcher& dispatcher; typename CallbackList::iterator state; Event (const Event& oth) = delete; OptionalRet next (add_const_ref_t... args) { return dispatcher.next(*this, args...); } }; void add_event_listener (const Callback& callback, bool back = true) { if (!callback) return; if (back) listeners.push_back(Wrapper(callback)); else listeners.push_front(Wrapper(callback)); } void add_event_listener (Callback&& callback, bool back = true) { if (!callback) return; if (back) listeners.push_back(Wrapper(std::forward(callback))); else listeners.push_front(Wrapper(std::forward(callback))); } void add (const SimpleCallback& callback, bool back = true) { if (!callback) return; if (back) listeners.push_back(Wrapper(callback)); else listeners.push_front(Wrapper(callback)); } void add (SimpleCallback&& callback, bool back = true) { if (!callback) return; if (back) listeners.push_back(Wrapper(std::forward(callback))); else listeners.push_front(Wrapper(std::forward(callback))); } template void prepend_event_listener (T&& callback) { add_event_listener(std::forward(callback), false); } template void prepend (T&& callback) { add(std::forward(callback), false); } auto operator() (add_const_ref_t... args) -> decltype(std::declval()(std::declval(), args...)) { auto iter = listeners.begin(); if (iter == listeners.end()) return optional_type::default_value(); Event e{*this, iter}; return (*iter)(e, args...); } template void remove (const SmthComparable& callback) { for (auto iter = listeners.rbegin(); iter != listeners.rend(); ++iter) { if (iter->equal(callback)) { listeners.erase(iter); break; } } } template void remove_object(T&& makable, decltype(function_details::tmp_abstract_function(std::forward(std::declval())))* = nullptr) { auto tmp = function_details::tmp_abstract_function(std::forward(makable)); remove(tmp); } template void remove_object(T&& makable, decltype(function_details::tmp_abstract_function(std::forward(std::declval())))* = nullptr) { auto tmp = function_details::tmp_abstract_function(std::forward(makable)); remove(tmp); } void remove_all () { listeners.clear(); } bool has_listeners () const { return listeners.size(); } private: template OptionalRet next (Event& e, RealArgs&&... args) { ++e.state; if (e.state != listeners.end()) { return (*e.state)(e, std::forward(args)...); } else { return optional_type::default_value(); } } CallbackList listeners; }; template class CallbackDispatcher : public CallbackDispatcher {}; }