diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index 35238eda2..67f2f3228 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -638,25 +638,23 @@ public: */ template 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) { - alloc_traits::construct(packed.second(), to_address(elem), Type{std::forward(args)...}); - } else { - alloc_traits::construct(packed.second(), to_address(elem), std::forward(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) { + alloc_traits::construct(packed.second(), to_address(assure_at_least(pos)), Type{std::forward(args)...}); + } else { + alloc_traits::construct(packed.second(), to_address(assure_at_least(pos)), std::forward(args)...); + } } ENTT_CATCH { - std::destroy_at(std::addressof(*elem)); + base_type::try_erase(entt); ENTT_THROW; } - return *elem; + return element_at(pos); } /** diff --git a/test/entt/entity/storage.cpp b/test/entt/entity/storage.cpp index 0964acfd3..1b7c7422b 100644 --- a/test/entt/entity/storage.cpp +++ b/test/entt/entity/storage.cpp @@ -55,6 +55,20 @@ private: entt::entity target{entt::null}; }; +struct crete_from_constructor { + crete_from_constructor(entt::storage &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: 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 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::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::trigger_on_allocate = true; - - ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator::exception_type); - ASSERT_FALSE(pool.contains(entt::entity{0})); - ASSERT_TRUE(pool.empty()); - test::throwing_allocator::trigger_on_allocate = true; ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator::exception_type); @@ -1626,6 +1645,12 @@ TEST(Storage, ThrowingAllocator) { ASSERT_FALSE(base.contains(entt::entity{0})); ASSERT_TRUE(base.empty()); + test::throwing_allocator::trigger_on_allocate = true; + + ASSERT_THROW(pool.emplace(entt::entity{0}, 0), test::throwing_allocator::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::trigger_after_allocate = true;