Răsfoiți Sursa

emitter works across boundaries now

Michele Caini 7 ani în urmă
părinte
comite
770e57c361
5 a modificat fișierele cu 65 adăugiri și 44 ștergeri
  1. 1 2
      TODO
  2. 1 13
      docs/lib.md
  3. 7 7
      src/entt/signal/dispatcher.hpp
  4. 51 22
      src/entt/signal/emitter.hpp
  5. 5 0
      test/entt/signal/emitter.cpp

+ 1 - 2
TODO

@@ -23,5 +23,4 @@
 * cleanup - see https://github.com/skypjack/entt/commit/ad5cedc08c83e8cbcc8aaeac9634d44624ffe35a#commitcomment-32380903
 
 ==> can we do more for shared libraries? who knows... see #144
-* to be updated: emitter
-* to be updated: doc
+* test across boundaries dispatcher and emitter

+ 1 - 13
docs/lib.md

@@ -11,7 +11,6 @@
 * [Macros, macros everywhere](#macros-macros-everywhere)
   * [Conflicts](#conflict)
 * [Allocations: the dark side of the force](#allocations-the-dark-side-of-the-force)
-* [Note](#note)
 <!--
 @endcond TURN_OFF_DOXYGEN
 -->
@@ -126,7 +125,7 @@ hashing strings during compilation. Therefore, conflicts are rare but still
 possible. In case of conflicts, everything simply will get broken at runtime and
 the strangest things will probably take place.<br/>
 Unfortunately, there is no safe way to prevent it. If this happens, it will be
-enough to give a different name to one of the conflicting types to solve the
+enough to give a different value to one of the conflicting types to solve the
 problem. To do this, users can either assign a different name to the class or
 directly define a specialization for the `shared_traits` class template.
 
@@ -154,14 +153,3 @@ enough).
 Maybe one day some dedicated methods will be added that do nothing but create a
 pool for a given type. Until now it has been preferred to keep the API cleaner
 as they are not strictly necessary.
-
-# Note
-
-I'm still working hard to make everything work across boundaries.<br/>
-The classes affected by the problem were `registry`, `dispatcher` and `emitter`.
-Currently, only `registry` and `dispatcher` fully support _shared types_. Using
-`emitter` across boundaries isn't allowed yet and can result in unexpected
-behavior on Windows in general and on GNU/Linux when default visibility is set
-to _hidden_.
-
-Stay tuned for future updates.

+ 7 - 7
src/entt/signal/dispatcher.hpp

@@ -91,7 +91,7 @@ class dispatcher {
     }
 
     template<typename Event>
-    signal_wrapper<Event> & wrapper() {
+    signal_wrapper<Event> & assure() {
         const auto wtype = type<Event>();
         wrapper_data *wdata = nullptr;
 
@@ -147,7 +147,7 @@ public:
      */
     template<typename Event>
     inline sink_type<Event> sink() ENTT_NOEXCEPT {
-        return wrapper<Event>().sink();
+        return assure<Event>().sink();
     }
 
     /**
@@ -162,7 +162,7 @@ public:
      */
     template<typename Event>
     inline void trigger(Event &&event, Args... args) {
-        wrapper<std::decay_t<Event>>().trigger(std::forward<Event>(event), args...);
+        assure<std::decay_t<Event>>().trigger(std::forward<Event>(event), args...);
     }
 
     /**
@@ -176,7 +176,7 @@ public:
      */
     template<typename Event>
     inline void trigger(Args... args) {
-        wrapper<std::decay_t<Event>>().trigger(Event{}, args...);
+        assure<std::decay_t<Event>>().trigger(Event{}, args...);
     }
 
     /**
@@ -191,7 +191,7 @@ public:
      */
     template<typename Event, typename... Params>
     inline void enqueue(Params &&... params) {
-        wrapper<Event>().enqueue(std::forward<Params>(params)...);
+        assure<Event>().enqueue(std::forward<Params>(params)...);
     }
 
     /**
@@ -205,7 +205,7 @@ public:
      */
     template<typename Event>
     inline void enqueue(Event &&event) {
-        wrapper<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
+        assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
     }
 
     /**
@@ -220,7 +220,7 @@ public:
      */
     template<typename Event>
     inline void update(Args... args) {
-        wrapper<Event>().publish(args...);
+        assure<Event>().publish(args...);
     }
 
     /**

+ 51 - 22
src/entt/signal/emitter.hpp

@@ -11,6 +11,7 @@
 #include <list>
 #include "../config/config.h"
 #include "../core/family.hpp"
+#include "../core/type_traits.hpp"
 
 
 namespace entt {
@@ -114,19 +115,51 @@ class emitter {
         container_type on_list{};
     };
 
+    struct handler_data {
+        std::unique_ptr<base_handler> handler;
+        ENTT_ID_TYPE runtime_type;
+    };
+
+    template<typename Event>
+    static auto type() ENTT_NOEXCEPT {
+        if constexpr(is_shared_v<Event>) {
+            return shared_traits<Event>::value;
+        } else {
+            return handler_family::type<Event>;
+        }
+    }
+
     template<typename Event>
-    event_handler<Event> & handler() ENTT_NOEXCEPT {
-        const auto family = handler_family::type<Event>;
+    event_handler<Event> * assure() const ENTT_NOEXCEPT {
+        const auto htype = type<Event>();
+        handler_data *hdata = nullptr;
+
+        if constexpr(is_shared_v<Event>) {
+            const auto it = std::find_if(handlers.begin(), handlers.end(), [htype](const auto &hdata) {
+                return hdata.handler && hdata.runtime_type == htype;
+            });
+
+            hdata = (it == handlers.cend() ? &handlers.emplace_back() : &(*it));
+        } else {
+            if(!(htype < handlers.size())) {
+                handlers.resize(htype+1);
+            }
+
+            hdata = &handlers[htype];
 
-        if(!(family < handlers.size())) {
-            handlers.resize(family+1);
+            if(hdata->handler && hdata->runtime_type != htype) {
+                handlers.emplace_back();
+                std::swap(handlers[htype], handlers.back());
+                hdata = &handlers[htype];
+            }
         }
 
-        if(!handlers[family]) {
-            handlers[family] = std::make_unique<event_handler<Event>>();
+        if(!hdata->handler) {
+            hdata->handler = std::make_unique<event_handler<Event>>();
+            hdata->runtime_type = htype;
         }
 
-        return static_cast<event_handler<Event> &>(*handlers[family]);
+        return static_cast<event_handler<Event> *>(hdata->handler.get());
     }
 
 public:
@@ -191,7 +224,7 @@ public:
      */
     template<typename Event, typename... Args>
     void publish(Args &&... args) {
-        handler<Event>().publish({ std::forward<Args>(args)... }, *static_cast<Derived *>(this));
+        assure<Event>()->publish({ std::forward<Args>(args)... }, *static_cast<Derived *>(this));
     }
 
     /**
@@ -216,7 +249,7 @@ public:
      */
     template<typename Event>
     connection<Event> on(listener<Event> listener) {
-        return handler<Event>().on(std::move(listener));
+        return assure<Event>()->on(std::move(listener));
     }
 
     /**
@@ -241,7 +274,7 @@ public:
      */
     template<typename Event>
     connection<Event> once(listener<Event> listener) {
-        return handler<Event>().once(std::move(listener));
+        return assure<Event>()->once(std::move(listener));
     }
 
     /**
@@ -255,7 +288,7 @@ public:
      */
     template<typename Event>
     void erase(connection<Event> conn) ENTT_NOEXCEPT {
-        handler<Event>().erase(std::move(conn));
+        assure<Event>()->erase(std::move(conn));
     }
 
     /**
@@ -268,7 +301,7 @@ public:
      */
     template<typename Event>
     void clear() ENTT_NOEXCEPT {
-        handler<Event>().clear();
+        assure<Event>()->clear();
     }
 
     /**
@@ -278,8 +311,8 @@ public:
      * results in undefined behavior.
      */
     void clear() ENTT_NOEXCEPT {
-        std::for_each(handlers.begin(), handlers.end(), [](auto &&handler) {
-            return handler ? handler->clear() : void();
+        std::for_each(handlers.begin(), handlers.end(), [](auto &&hdata) {
+            return hdata.handler ? hdata.handler->clear() : void();
         });
     }
 
@@ -290,11 +323,7 @@ public:
      */
     template<typename Event>
     bool empty() const ENTT_NOEXCEPT {
-        const auto family = handler_family::type<Event>;
-
-        return (!(family < handlers.size()) ||
-                !handlers[family] ||
-                static_cast<event_handler<Event> &>(*handlers[family]).empty());
+        return assure<Event>()->empty();
     }
 
     /**
@@ -302,13 +331,13 @@ public:
      * @return True if there are no listeners registered, false otherwise.
      */
     bool empty() const ENTT_NOEXCEPT {
-        return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&handler) {
-            return !handler || handler->empty();
+        return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&hdata) {
+            return !hdata.handler || hdata.handler->empty();
         });
     }
 
 private:
-    std::vector<std::unique_ptr<base_handler>> handlers{};
+    mutable std::vector<handler_data> handlers{};
 };
 
 

+ 5 - 0
test/entt/signal/emitter.cpp

@@ -1,15 +1,20 @@
 #include <gtest/gtest.h>
+#include <entt/entity/entt_traits.hpp>
 #include <entt/signal/emitter.hpp>
 
 struct test_emitter: entt::emitter<test_emitter> {};
 
 struct foo_event { int i; char c; };
 struct bar_event {};
+struct quux_event {};
+
+ENTT_SHARED_TYPE(foo_event)
 
 TEST(Emitter, Clear) {
     test_emitter emitter;
 
     ASSERT_TRUE(emitter.empty());
+    ASSERT_TRUE(emitter.empty<quux_event>());
 
     emitter.on<foo_event>([](const auto &, const auto &){});