Bladeren bron

emitter: allocator support

Michele Caini 3 jaren geleden
bovenliggende
commit
8f84bd7091
4 gewijzigde bestanden met toevoegingen van 111 en 22 verwijderingen
  1. 1 2
      TODO
  2. 67 16
      src/entt/signal/emitter.hpp
  3. 1 1
      src/entt/signal/fwd.hpp
  4. 42 3
      test/entt/signal/emitter.cpp

+ 1 - 2
TODO

@@ -12,14 +12,13 @@ DOC:
 * update entity doc when the storage based model is in place
 
 WIP:
+* any: assert-if-dynamic like tag to the constructor
 * no gh-pages, use main/docs or similar, see settings/pages on gh
 * sparse set/storage support for move-only types, internal rework required
 * get rid of observers, storage based views made them pointless - document alternatives
 * add storage getter for filters to views and groups
 * exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details)
-* emitter: runtime handlers, allocator support (ready for both already)
 * basic_storage::bind for cross-registry setups (see and remove todo from entity_copy.cpp)
-* uses-allocator construction: any (with allocator support), poly, ...
 * process scheduler: reviews, use free lists internally
 * iterator based try_emplace vs try_insert for perf reasons
 * dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model

+ 67 - 16
src/entt/signal/emitter.hpp

@@ -29,29 +29,80 @@ namespace entt {
  * to itself to its listeners.
  *
  * @tparam Derived Emitter type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Derived>
+template<typename Derived, typename Allocator>
 class emitter {
-    using function_type = std::function<void(void *)>;
+    using key_type = id_type;
+    using mapped_type = std::function<void(void *)>;
+
+    using alloc_traits = std::allocator_traits<Allocator>;
+    using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
+    using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<key_type>, container_allocator>;
 
 public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+
     /*! @brief Default constructor. */
     emitter()
-        : handlers{} {}
+        : emitter{allocator_type{}} {}
+
+    /**
+     * @brief Constructs an emitter with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit emitter(const allocator_type &allocator)
+        : handlers{allocator, allocator} {}
 
     /*! @brief Default destructor. */
     virtual ~emitter() noexcept {
         static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Invalid emitter type");
     }
 
-    /*! @brief Default move constructor. */
-    emitter(emitter &&) noexcept = default;
+    /**
+     * @brief Move constructor.
+     * @param other The instance to move from.
+     */
+    emitter(emitter &&other) noexcept
+        : handlers{std::move(other.handlers)} {}
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    emitter(emitter &&other, const allocator_type &allocator) noexcept
+        : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {}
 
     /**
-     * @brief Default move assignment operator.
-     * @return This emitter.
+     * @brief Move assignment operator.
+     * @param other The instance to move from.
+     * @return This dispatcher.
      */
-    emitter &operator=(emitter &&) noexcept = default;
+    emitter &operator=(emitter &&other) noexcept {
+        handlers = std::move(other.handlers);
+        return *this;
+    }
+
+    /**
+     * @brief Exchanges the contents with those of a given emitter.
+     * @param other Emitter to exchange the content with.
+     */
+    void swap(emitter &other) {
+        using std::swap;
+        swap(handlers, other.handlers);
+    }
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return handlers.second();
+    }
 
     /**
      * @brief Publishes a given event.
@@ -60,8 +111,8 @@ public:
      */
     template<typename Type>
     void publish(Type &&value) {
-        if(const auto id = type_id<Type>().hash(); handlers.contains(id)) {
-            handlers[id](&value);
+        if(const auto id = type_id<Type>().hash(); handlers.first().contains(id)) {
+            handlers.first()[id](&value);
         }
     }
 
@@ -72,7 +123,7 @@ public:
      */
     template<typename Type>
     void on(std::function<void(Type &, Derived &)> func) {
-        handlers.insert_or_assign(type_id<Type>().hash(), [func = std::move(func), this](void *value) {
+        handlers.first().insert_or_assign(type_id<Type>().hash(), [func = std::move(func), this](void *value) {
             func(*static_cast<Type *>(value), static_cast<Derived &>(*this));
         });
     }
@@ -83,12 +134,12 @@ public:
      */
     template<typename Type>
     void erase() {
-        handlers.erase(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
+        handlers.first().erase(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
     }
 
     /*! @brief Disconnects all the listeners. */
     void clear() noexcept {
-        handlers.clear();
+        handlers.first().clear();
     }
 
     /**
@@ -98,7 +149,7 @@ public:
      */
     template<typename Type>
     [[nodiscard]] bool contains() const {
-        return handlers.contains(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
+        return handlers.first().contains(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
     }
 
     /**
@@ -106,11 +157,11 @@ public:
      * @return True if there are no listeners registered, false otherwise.
      */
     [[nodiscard]] bool empty() const noexcept {
-        return handlers.empty();
+        return handlers.first().empty();
     }
 
 private:
-    dense_map<id_type, function_type, identity> handlers{};
+    compressed_pair<container_type, allocator_type> handlers;
 };
 
 } // namespace entt

+ 1 - 1
src/entt/signal/fwd.hpp

@@ -11,7 +11,7 @@ class delegate;
 template<typename = std::allocator<void>>
 class basic_dispatcher;
 
-template<typename>
+template<typename, typename = std::allocator<void>>
 class emitter;
 
 class connection;

+ 42 - 3
test/entt/signal/emitter.cpp

@@ -3,11 +3,12 @@
 #include <gtest/gtest.h>
 #include <entt/signal/emitter.hpp>
 
-struct test_emitter: entt::emitter<test_emitter> {};
+struct test_emitter: entt::emitter<test_emitter> {
+    using entt::emitter<test_emitter>::emitter;
+};
 
 struct foo_event {
     int i;
-    char c;
 };
 
 struct bar_event {};
@@ -33,6 +34,30 @@ TEST(Emitter, Move) {
     ASSERT_TRUE(other.empty());
 }
 
+TEST(Emitter, Swap) {
+    test_emitter emitter;
+    test_emitter other;
+    int value{};
+
+    emitter.on<foo_event>([&value](auto &event, const auto &) {
+        value = event.i;
+    });
+
+    ASSERT_FALSE(emitter.empty());
+    ASSERT_TRUE(other.empty());
+
+    emitter.swap(other);
+    emitter.publish(foo_event{42});
+
+    ASSERT_EQ(value, 0);
+    ASSERT_TRUE(emitter.empty());
+    ASSERT_FALSE(other.empty());
+
+    other.publish(foo_event{42});
+
+    ASSERT_EQ(value, 42);
+}
+
 TEST(Emitter, Clear) {
     test_emitter emitter;
 
@@ -110,7 +135,7 @@ TEST(Emitter, On) {
     ASSERT_TRUE(emitter.contains<foo_event>());
     ASSERT_EQ(value, 0);
 
-    emitter.publish(foo_event{42, 'c'});
+    emitter.publish(foo_event{42});
 
     ASSERT_EQ(value, 42);
 }
@@ -129,3 +154,17 @@ TEST(Emitter, OnAndErase) {
     ASSERT_TRUE(emitter.empty());
     ASSERT_FALSE(emitter.contains<bar_event>());
 }
+
+TEST(Emitter, CustomAllocator) {
+    std::allocator<void> allocator;
+    test_emitter emitter{allocator};
+
+    ASSERT_EQ(emitter.get_allocator(), allocator);
+    ASSERT_FALSE(emitter.get_allocator() != allocator);
+
+    emitter.on<foo_event>([](auto &, const auto &) {});
+    decltype(emitter) other{std::move(emitter), allocator};
+
+    ASSERT_TRUE(emitter.empty());
+    ASSERT_FALSE(other.empty());
+}