Browse Source

added support for extra arguments to dispatcher

Michele Caini 7 years ago
parent
commit
d119032cb9
3 changed files with 60 additions and 46 deletions
  1. 9 3
      TODO
  2. 41 33
      src/entt/signal/dispatcher.hpp
  3. 10 10
      test/entt/signal/dispatcher.cpp

+ 9 - 3
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

+ 41 - 33
src/entt/signal/dispatcher.hpp

@@ -5,7 +5,6 @@
 #include <vector>
 #include <memory>
 #include <utility>
-#include <algorithm>
 #include <type_traits>
 #include "../config/config.h"
 #include "../core/family.hpp"
@@ -22,13 +21,17 @@ namespace entt {
  * events to be published all together once per tick.<br/>
  * 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<typename... Args>
 class dispatcher final {
     using event_family = family<struct internal_dispatcher_event_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<typename Event>
     struct signal_wrapper final: base_wrapper {
-        using sink_type = typename sigh<void(const Event &)>::sink_type;
+        using signal_type = sigh<void(const Event &, Args...)>;
+        using sink_type = typename signal_type::sink_type;
+
+        void publish(Args... args) override {
+            for(const auto &event: events[current]) {
+                signal.publish(event, args...);
+            }
 
-        void publish() override {
-            const auto &curr = current++;
-            current %= std::extent<decltype(events)>::value;
-            std::for_each(events[curr].cbegin(), events[curr].cend(), [this](const auto &event) { signal.publish(event); });
-            events[curr].clear();
+            events[current].clear();
+            current = (current + 1) % std::extent<decltype(events)>::value;
         }
 
         inline sink_type sink() ENTT_NOEXCEPT {
             return signal.sink();
         }
 
-        template<typename... Args>
-        inline void trigger(Args &&... args) {
-            signal.publish({ std::forward<Args>(args)... });
+        inline void trigger(const Event &event, Args... args) {
+            signal.publish(event, args...);
         }
 
-        template<typename... Args>
-        inline void enqueue(Args &&... args) {
-            events[current].push_back({ std::forward<Args>(args)... });
+        template<typename... Params>
+        inline void enqueue(Params &&... params) {
+            events[current].emplace_back(std::forward<Params>(params)...);
         }
 
     private:
-        sigh<void(const Event &)> signal{};
+        signal_type signal{};
         std::vector<Event> 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<typename Event, typename... Args>
-    inline void trigger(Args &&... args) {
-        wrapper<Event>().trigger(std::forward<Args>(args)...);
+    template<typename Event>
+    inline void trigger(Event &&event, Args... args) {
+        wrapper<std::decay_t<Event>>().trigger(std::forward<Event>(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<typename Event>
-    inline void trigger(Event &&event) {
-        wrapper<std::decay_t<Event>>().trigger(std::forward<Event>(event));
+    inline void trigger(Args... args) {
+        wrapper<std::decay_t<Event>>().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<typename Event, typename... Args>
-    inline void enqueue(Args &&... args) {
-        wrapper<Event>().enqueue(std::forward<Args>(args)...);
+    template<typename Event, typename... Params>
+    inline void enqueue(Params &&... params) {
+        wrapper<Event>().enqueue(std::forward<Params>(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<typename Event>
-    inline void update() {
-        wrapper<Event>().publish();
+    inline void update(Args... args) {
+        wrapper<Event>().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...);
             }
         }
     }

+ 10 - 10
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<int> dispatcher;
     receiver receiver;
 
     dispatcher.sink<an_event>().connect(&receiver);
-    dispatcher.trigger<an_event>();
+    dispatcher.trigger<an_event>(1);
     dispatcher.enqueue<an_event>();
     dispatcher.enqueue<another_event>();
-    dispatcher.update<another_event>();
+    dispatcher.update<another_event>(1);
 
     ASSERT_EQ(receiver.cnt, 1);
 
-    dispatcher.update<an_event>();
-    dispatcher.trigger<an_event>();
+    dispatcher.update<an_event>(2);
+    dispatcher.trigger<an_event>(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<an_event>().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);
 }