Просмотр исходного кода

storage: support chained constructors i.e. parent-to-child propagation

Michele Caini 4 лет назад
Родитель
Сommit
8a19e8dafe
2 измененных файлов с 41 добавлено и 18 удалено
  1. 10 12
      src/entt/entity/storage.hpp
  2. 31 6
      test/entt/entity/storage.cpp

+ 10 - 12
src/entt/entity/storage.hpp

@@ -638,25 +638,23 @@ public:
      */
     template<typename... Args>
     value_type &emplace(const entity_type entt, Args &&...args) {
-        const auto pos = base_type::slot();
-        auto elem = assure_at_least(pos);
-
-        if constexpr(std::is_aggregate_v<value_type>) {
-            alloc_traits::construct(packed.second(), to_address(elem), Type{std::forward<Args>(args)...});
-        } else {
-            alloc_traits::construct(packed.second(), to_address(elem), std::forward<Args>(args)...);
-        }
+        // support chained constructors i.e. parent-to-child propagation
+        base_type::try_emplace(entt);
+        const auto pos = base_type::index(entt);
 
         ENTT_TRY {
-            base_type::try_emplace(entt);
-            ENTT_ASSERT(pos == base_type::index(entt), "Misplaced component");
+            if constexpr(std::is_aggregate_v<value_type>) {
+                alloc_traits::construct(packed.second(), to_address(assure_at_least(pos)), Type{std::forward<Args>(args)...});
+            } else {
+                alloc_traits::construct(packed.second(), to_address(assure_at_least(pos)), std::forward<Args>(args)...);
+            }
         }
         ENTT_CATCH {
-            std::destroy_at(std::addressof(*elem));
+            base_type::try_erase(entt);
             ENTT_THROW;
         }
 
-        return *elem;
+        return element_at(pos);
     }
 
     /**

+ 31 - 6
test/entt/entity/storage.cpp

@@ -55,6 +55,20 @@ private:
     entt::entity target{entt::null};
 };
 
+struct crete_from_constructor {
+    crete_from_constructor(entt::storage<crete_from_constructor> &ref, entt::entity other)
+        : child{other} {
+        if(child != entt::null) {
+            ref.emplace(child, ref, entt::null);
+        }
+    }
+
+    crete_from_constructor(crete_from_constructor &&other) ENTT_NOEXCEPT = default;
+    crete_from_constructor &operator=(crete_from_constructor &&other) ENTT_NOEXCEPT = default;
+
+    entt::entity child;
+};
+
 template<>
 struct entt::component_traits<stable_type>: basic_component_traits {
     static constexpr auto in_place_delete = true;
@@ -1537,6 +1551,17 @@ TEST(Storage, UpdateFromDestructor) {
     test(entt::entity{0u});
 }
 
+TEST(Storage, CreateFromConstructor) {
+    entt::storage<crete_from_constructor> pool;
+    const entt::entity entity{0u};
+    const entt::entity other{1u};
+
+    pool.emplace(entity, pool, other);
+
+    ASSERT_EQ(pool.get(entity).child, other);
+    ASSERT_EQ(pool.get(other).child, static_cast<entt::entity>(entt::null));
+}
+
 TEST(Storage, CustomAllocator) {
     auto test = [](auto pool, auto alloc) {
         ASSERT_EQ(pool.get_allocator(), alloc);
@@ -1608,12 +1633,6 @@ TEST(Storage, ThrowingAllocator) {
 
     ASSERT_EQ(pool.capacity(), 0u);
 
-    test::throwing_allocator<int>::trigger_on_allocate = true;
-
-    ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator<int>::exception_type);
-    ASSERT_FALSE(pool.contains(entt::entity{0}));
-    ASSERT_TRUE(pool.empty());
-
     test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
 
     ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator<entt::entity>::exception_type);
@@ -1626,6 +1645,12 @@ TEST(Storage, ThrowingAllocator) {
     ASSERT_FALSE(base.contains(entt::entity{0}));
     ASSERT_TRUE(base.empty());
 
+    test::throwing_allocator<int>::trigger_on_allocate = true;
+
+    ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator<int>::exception_type);
+    ASSERT_FALSE(pool.contains(entt::entity{0}));
+    ASSERT_TRUE(pool.empty());
+
     pool.emplace(entt::entity{0}, 0);
     const entt::entity entities[2u]{entt::entity{1}, entt::entity{ENTT_SPARSE_PAGE}};
     test::throwing_allocator<entt::entity>::trigger_after_allocate = true;