Browse Source

sparse_set/storage: emplace-from-below with value

Michele Caini 4 years ago
parent
commit
cd6c60fd46
3 changed files with 64 additions and 12 deletions
  1. 4 3
      src/entt/entity/sparse_set.hpp
  2. 16 5
      src/entt/entity/storage.hpp
  3. 44 4
      test/entt/entity/storage.cpp

+ 4 - 3
src/entt/entity/sparse_set.hpp

@@ -241,7 +241,7 @@ protected:
      * @brief Assigns an entity to a sparse set.
      * @param entt A valid identifier.
      */
-    virtual void try_emplace(const Entity entt) {
+    virtual void try_emplace(const Entity entt, const void * = nullptr) {
         const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
         const auto page = pos / sparse_page_v;
 
@@ -588,9 +588,10 @@ public:
      * results in undefined behavior.
      *
      * @param entt A valid identifier.
+     * @param value Optional opaque value to forward to mixins, if any.
      */
-    void emplace(const entity_type entt) {
-        try_emplace(entt);
+    void emplace(const entity_type entt, const void *value = nullptr) {
+        try_emplace(entt, value);
         ENTT_ASSERT(contains(entt), "Emplace did not take place");
     }
 

+ 16 - 5
src/entt/entity/storage.hpp

@@ -347,11 +347,12 @@ protected:
     /**
      * @brief Assigns an entity to a storage.
      * @param entt A valid identifier.
+     * @param value Optional opaque value.
      */
-    void try_emplace([[maybe_unused]] const Entity entt) override {
-        if constexpr(std::is_default_constructible_v<value_type>) {
+    void try_emplace(const Entity entt, [[maybe_unused]] const void *value) override {
+        [[maybe_unused]] const auto try_emplace_with_args = [this, entt](auto &&...args) {
             const auto pos = base_type::slot();
-            construct(assure_at_least(pos));
+            construct(assure_at_least(pos), std::forward<decltype(args)>(args)...);
 
             ENTT_TRY {
                 base_type::try_emplace(entt);
@@ -361,6 +362,16 @@ protected:
                 std::destroy_at(std::addressof(element_at(pos)));
                 ENTT_THROW;
             }
+        };
+
+        if(value) {
+            if constexpr(std::is_copy_constructible_v<value_type>) {
+                try_emplace_with_args(*static_cast<const value_type *>(value));
+            }
+        } else {
+            if constexpr(std::is_default_constructible_v<value_type>) {
+                try_emplace_with_args();
+            }
         }
     }
 
@@ -869,9 +880,9 @@ class sigh_storage_mixin final: public Type {
     }
 
     /*! @copydoc basic_sparse_set::try_emplace */
-    void try_emplace(const typename Type::entity_type entt) final {
+    void try_emplace(const typename Type::entity_type entt, const void *value) final {
         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
-        Type::try_emplace(entt);
+        Type::try_emplace(entt, value);
         construction.publish(*owner, entt);
     }
 

+ 44 - 4
test/entt/entity/storage.cpp

@@ -577,17 +577,19 @@ TEST(Storage, TypeFromBase) {
     ASSERT_FALSE(pool.contains(entities[0u]));
     ASSERT_FALSE(pool.contains(entities[1u]));
 
-    base.emplace(entities[0u]);
+    int instance = 42;
+    base.emplace(entities[0u], &instance);
 
     ASSERT_TRUE(pool.contains(entities[0u]));
     ASSERT_FALSE(pool.contains(entities[1u]));
-    ASSERT_EQ(pool.get(entities[0u]), 0);
+    ASSERT_EQ(pool.get(entities[0u]), 42);
 
     base.erase(entities[0u]);
     base.insert(std::begin(entities), std::end(entities));
 
     ASSERT_TRUE(pool.contains(entities[0u]));
     ASSERT_TRUE(pool.contains(entities[1u]));
+    ASSERT_EQ(pool.get(entities[0u]), 0);
     ASSERT_EQ(pool.get(entities[1u]), 0);
 
     base.erase(std::begin(entities), std::end(entities));
@@ -603,7 +605,8 @@ TEST(Storage, EmptyTypeFromBase) {
     ASSERT_FALSE(pool.contains(entities[0u]));
     ASSERT_FALSE(pool.contains(entities[1u]));
 
-    base.emplace(entities[0u]);
+    empty_stable_type instance{};
+    base.emplace(entities[0u], &instance);
 
     ASSERT_TRUE(pool.contains(entities[0u]));
     ASSERT_FALSE(pool.contains(entities[1u]));
@@ -637,7 +640,8 @@ TEST(Storage, NonDefaultConstructibleTypeFromBase) {
     ASSERT_EQ(base.find(entities[0u]), base.end());
     ASSERT_TRUE(pool.empty());
 
-    pool.emplace(entities[0u], 3);
+    non_default_constructible instance{3};
+    base.emplace(entities[0u], &instance);
 
     ASSERT_TRUE(pool.contains(entities[0u]));
     ASSERT_FALSE(pool.contains(entities[1u]));
@@ -656,6 +660,42 @@ TEST(Storage, NonDefaultConstructibleTypeFromBase) {
     ASSERT_TRUE(pool.empty());
 }
 
+TEST(Storage, NonCopyConstructibleTypeFromBase) {
+    entt::storage<std::unique_ptr<int>> pool;
+    entt::sparse_set &base = pool;
+    entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
+
+    ASSERT_FALSE(pool.contains(entities[0u]));
+    ASSERT_FALSE(pool.contains(entities[1u]));
+
+    base.emplace(entities[0u]);
+
+    ASSERT_TRUE(pool.contains(entities[0u]));
+    ASSERT_FALSE(pool.contains(entities[1u]));
+    ASSERT_NE(base.find(entities[0u]), base.end());
+    ASSERT_FALSE(pool.empty());
+
+    std::unique_ptr<int> instance = std::make_unique<int>(3);
+
+    ASSERT_DEATH(base.emplace(entities[1u], &instance), "");
+
+    ASSERT_TRUE(pool.contains(entities[0u]));
+    ASSERT_FALSE(pool.contains(entities[1u]));
+
+    base.erase(entities[0u]);
+
+    ASSERT_TRUE(pool.empty());
+    ASSERT_FALSE(pool.contains(entities[0u]));
+
+    base.insert(std::begin(entities), std::end(entities));
+
+    ASSERT_TRUE(pool.contains(entities[0u]));
+    ASSERT_TRUE(pool.contains(entities[1u]));
+    ASSERT_NE(base.find(entities[0u]), base.end());
+    ASSERT_NE(base.find(entities[1u]), base.end());
+    ASSERT_FALSE(pool.empty());
+}
+
 TEST(Storage, Compact) {
     entt::storage<stable_type> pool;