diff --git a/TODO b/TODO index 225c642d2..9c05495ee 100644 --- a/TODO +++ b/TODO @@ -8,7 +8,6 @@ * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc) * runner proposal: https://en.wikipedia.org/wiki/Fork%E2%80%93join_model https://slide-rs.github.io/specs/03_dispatcher.html * is it possible to iterate all the components assigned to an entity through a common base class? -* can we do more for shared libraries? who knows... see #144 * work stealing job system (see #100) * meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects * meta: move-to-head optimization when searching by name on parts (data, func, etc) @@ -16,10 +15,17 @@ * destroy overload that accepts a couple of iterators (see create) * allow for built-in parallel each if possible * add on-the-fly sort functionality (is it possible?) -* write/show how to create an archetype based model on top of EnTT * write/show how to create prefabs on top of EnTT (that's easy!!) * mention hunter in the readme file, section packaging tools * travis + windows is now available, try it * events on replace, so that one can track updated components? indagate impact -* optimize for empty component - maybe it's possible with a raw-less view that uses fake iterators (with correct size (correct size, same component) * tags revenge: if it's possible, reintroduce them but without a link to entities (see #169 for more details) + +Required test: +* can we do more for shared libraries? who knows... see #144 +* type_info::hash_code can be used in place of family when it comes to working with dlls + +Ready to go: +* stealing archetype-like model on top of EnTT (design completed, wip dev) +* write/show how to create an archetype based model on top of EnTT +* do not include it in EnTT, it breaks the pay-for-what-you-use principle all the ways diff --git a/src/entt/signal/dispatcher.hpp b/src/entt/signal/dispatcher.hpp index 80782cc49..1ae6e473e 100644 --- a/src/entt/signal/dispatcher.hpp +++ b/src/entt/signal/dispatcher.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include "../config/config.h" #include "../core/family.hpp" @@ -22,13 +21,17 @@ namespace entt { * events to be published all together once per tick.
* Listeners are provided in the form of member functions. For each event of * type `Event`, listeners are such that they can be invoked with an argument of - * type `const Event &`, no matter what the return type is. + * type `const Event &` plus an extra list of arguments to forward with the + * event itself, no matter what the return type is. * * Member functions named `receive` are automatically detected and registered or * unregistered by the dispatcher. The type of the instances is `Class *` (a * naked pointer). It means that users must guarantee that the lifetimes of the * instances overcome the one of the dispatcher itself to avoid crashes. + * + * @tparam Args Types of arguments to forward along with an event. */ +template class dispatcher final { using event_family = family; @@ -37,36 +40,38 @@ class dispatcher final { struct base_wrapper { virtual ~base_wrapper() = default; - virtual void publish() = 0; + virtual void publish(Args...) = 0; }; template struct signal_wrapper final: base_wrapper { - using sink_type = typename sigh::sink_type; + using signal_type = sigh; + using sink_type = typename signal_type::sink_type; - void publish() override { - const auto &curr = current++; - current %= std::extent::value; - std::for_each(events[curr].cbegin(), events[curr].cend(), [this](const auto &event) { signal.publish(event); }); - events[curr].clear(); + void publish(Args... args) override { + for(const auto &event: events[current]) { + signal.publish(event, args...); + } + + events[current].clear(); + current = (current + 1) % std::extent::value; } inline sink_type sink() ENTT_NOEXCEPT { return signal.sink(); } - template - inline void trigger(Args &&... args) { - signal.publish({ std::forward(args)... }); + inline void trigger(const Event &event, Args... args) { + signal.publish(event, args...); } - template - inline void enqueue(Args &&... args) { - events[current].push_back({ std::forward(args)... }); + template + inline void enqueue(Params &&... params) { + events[current].emplace_back(std::forward(params)...); } private: - sigh signal{}; + signal_type signal{}; std::vector events[2]; int current{}; }; @@ -120,12 +125,12 @@ public: * The event is discarded after the execution. * * @tparam Event Type of event to trigger. - * @tparam Args Types of arguments to use to construct the event. - * @param args Arguments to use to construct the event. + * @param event An instance of the given type of event. + * @param args Arguments to forward along with the event. */ - template - inline void trigger(Args &&... args) { - wrapper().trigger(std::forward(args)...); + template + inline void trigger(Event &&event, Args... args) { + wrapper>().trigger(std::forward(event), args...); } /** @@ -135,11 +140,11 @@ public: * The event is discarded after the execution. * * @tparam Event Type of event to trigger. - * @param event An instance of the given type of event. + * @param args Arguments to forward along with the event. */ template - inline void trigger(Event &&event) { - wrapper>().trigger(std::forward(event)); + inline void trigger(Args... args) { + wrapper>().trigger(Event{}, args...); } /** @@ -149,12 +154,12 @@ public: * `update` member function to notify listeners when ready. * * @tparam Event Type of event to enqueue. - * @tparam Args Types of arguments to use to construct the event. - * @param args Arguments to use to construct the event. + * @tparam Params Types of arguments to use to construct the event. + * @param params Arguments to use to construct the event. */ - template - inline void enqueue(Args &&... args) { - wrapper().enqueue(std::forward(args)...); + template + inline void enqueue(Params &&... params) { + wrapper().enqueue(std::forward(params)...); } /** @@ -179,10 +184,11 @@ public: * to reduce at a minimum the time spent in the bodies of the listeners. * * @tparam Event Type of events to send. + * @param args Arguments to forward along with the event. */ template - inline void update() { - wrapper().publish(); + inline void update(Args... args) { + wrapper().publish(args...); } /** @@ -191,13 +197,15 @@ public: * This method is blocking and it doesn't return until all the events are * delivered to the registered listeners. It's responsibility of the users * to reduce at a minimum the time spent in the bodies of the listeners. + * + * @param args Arguments to forward along with the event. */ - inline void update() const { + inline void update(Args... args) const { for(auto pos = wrappers.size(); pos; --pos) { auto &wrapper = wrappers[pos-1]; if(wrapper) { - wrapper->publish(); + wrapper->publish(args...); } } } diff --git a/test/entt/signal/dispatcher.cpp b/test/entt/signal/dispatcher.cpp index 4a2102e66..90ed3f905 100644 --- a/test/entt/signal/dispatcher.cpp +++ b/test/entt/signal/dispatcher.cpp @@ -6,27 +6,27 @@ struct an_event {}; struct another_event {}; struct receiver { - void receive(const an_event &) { ++cnt; } + void receive(const an_event &, int value) { cnt += value; } void reset() { cnt = 0; } int cnt{0}; }; TEST(Dispatcher, Functionalities) { - entt::dispatcher dispatcher; + entt::dispatcher dispatcher; receiver receiver; dispatcher.sink().connect(&receiver); - dispatcher.trigger(); + dispatcher.trigger(1); dispatcher.enqueue(); dispatcher.enqueue(); - dispatcher.update(); + dispatcher.update(1); ASSERT_EQ(receiver.cnt, 1); - dispatcher.update(); - dispatcher.trigger(); + dispatcher.update(2); + dispatcher.trigger(1); - ASSERT_EQ(receiver.cnt, 3); + ASSERT_EQ(receiver.cnt, 4); receiver.reset(); @@ -34,10 +34,10 @@ TEST(Dispatcher, Functionalities) { const an_event &cevent = event; dispatcher.sink().disconnect(&receiver); - dispatcher.trigger(an_event{}); + dispatcher.trigger(an_event{}, 1); dispatcher.enqueue(event); - dispatcher.update(); - dispatcher.trigger(cevent); + dispatcher.update(1); + dispatcher.trigger(cevent, 1); ASSERT_EQ(receiver.cnt, 0); }