From e5d1ca2e3cb19e620f464dbea499271ea92a8151 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Sat, 13 Nov 2021 18:54:48 +0100 Subject: [PATCH] registry: * const thread safe implementation * views created from const registires can refer placeholder pools (that is, they don't refresh) --- src/entt/entity/registry.hpp | 133 +++++++++++++++++------------------ test/entt/entity/view.cpp | 32 ++++----- 2 files changed, 81 insertions(+), 84 deletions(-) diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index f98123736..0153e02ec 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -63,20 +63,20 @@ class basic_registry { template void maybe_valid_if(basic_registry &owner, const Entity entt) { - [[maybe_unused]] const auto cpools = std::make_tuple(owner.assure()...); + [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); - const auto is_valid = ((std::is_same_v || std::get *>(cpools)->contains(entt)) && ...) - && ((std::is_same_v || owner.assure()->contains(entt)) && ...) - && ((std::is_same_v || !owner.assure()->contains(entt)) && ...); + const auto is_valid = ((std::is_same_v || std::get &>(cpools).contains(entt)) && ...) + && ((std::is_same_v || owner.assure().contains(entt)) && ...) + && ((std::is_same_v || !owner.assure().contains(entt)) && ...); if constexpr(sizeof...(Owned) == 0) { if(is_valid && !current.contains(entt)) { current.emplace(entt); } } else { - if(is_valid && !(std::get<0>(cpools)->index(entt) < current)) { + if(is_valid && !(std::get<0>(cpools).index(entt) < current)) { const auto pos = current++; - (std::get *>(cpools)->swap_elements(std::get *>(cpools)->data()[pos], entt), ...); + (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); } } } @@ -85,9 +85,9 @@ class basic_registry { if constexpr(sizeof...(Owned) == 0) { current.remove(entt); } else { - if(const auto cpools = std::make_tuple(owner.assure()...); std::get<0>(cpools)->contains(entt) && (std::get<0>(cpools)->index(entt) < current)) { + if(const auto cpools = std::forward_as_tuple(owner.assure()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { const auto pos = --current; - (std::get *>(cpools)->swap_elements(std::get *>(cpools)->data()[pos], entt), ...); + (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); } } } @@ -102,24 +102,29 @@ class basic_registry { }; template - [[nodiscard]] storage_type *assure() const { + [[nodiscard]] storage_type &assure() { static_assert(std::is_same_v>, "Non-decayed types not allowed"); auto &&pdata = pools[type_id().hash()]; if(!pdata.pool) { pdata.pool.reset(new storage_type{}); - pdata.pool->context(forward_as_any(const_cast(*this))); + pdata.pool->context(forward_as_any(*this)); pdata.info = &type_id(); } - return static_cast *>(pdata.pool.get()); + return static_cast &>(*pdata.pool); } template - [[nodiscard]] const storage_type *pool_if_exists() const ENTT_NOEXCEPT { + [[nodiscard]] const storage_type &assure() const { static_assert(std::is_same_v>, "Non-decayed types not allowed"); - const auto it = pools.find(type_id().hash()); - return (it == pools.end()) ? nullptr : static_cast *>(it->second.pool.get()); + + if(const auto it = pools.find(type_id().hash()); it != pools.cend()) { + return static_cast &>(*it->second.pool); + } + + static storage_type placeholder{}; + return placeholder; } auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { @@ -203,8 +208,7 @@ public: */ template [[nodiscard]] size_type size() const { - const auto *cpool = pool_if_exists>(); - return cpool ? cpool->size() : size_type{}; + return assure>().size(); } /** @@ -247,7 +251,7 @@ public: if constexpr(sizeof...(Component) == 0) { entities.reserve(cap); } else { - (assure()->reserve(cap), ...); + (assure().reserve(cap), ...); } } @@ -258,8 +262,7 @@ public: */ template [[nodiscard]] size_type capacity() const { - const auto *cpool = pool_if_exists>(); - return cpool ? cpool->capacity() : size_type{}; + return assure>().capacity(); } /** @@ -278,7 +281,7 @@ public: */ template void shrink_to_fit() { - (assure()->shrink_to_fit(), ...); + (assure().shrink_to_fit(), ...); } /** @@ -297,7 +300,7 @@ public: if constexpr(sizeof...(Component) == 0) { return !alive(); } else { - return [](const auto *...cpool) { return ((!cpool || cpool->empty()) && ...); }(pool_if_exists>()...); + return (assure>().empty() && ...); } } @@ -576,7 +579,7 @@ public: template decltype(auto) emplace(const entity_type entity, Args &&...args) { ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure()->emplace(entity, std::forward(args)...); + return assure().emplace(entity, std::forward(args)...); } /** @@ -593,7 +596,7 @@ public: template void insert(It first, It last, const Component &value = {}) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure()->insert(first, last, value); + assure().insert(first, last, value); } /** @@ -612,7 +615,7 @@ public: void insert(EIt first, EIt last, CIt from) { static_assert(std::is_constructible_v::value_type>, "Invalid value type"); ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure()->insert(first, last, from); + assure().insert(first, last, from); } /** @@ -630,11 +633,11 @@ public: template decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) { ENTT_ASSERT(valid(entity), "Invalid entity"); - auto *cpool = assure(); + auto &cpool = assure(); - return cpool->contains(entity) - ? cpool->patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }) - : cpool->emplace(entity, std::forward(args)...); + return cpool.contains(entity) + ? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }) + : cpool.emplace(entity, std::forward(args)...); } /** @@ -664,7 +667,7 @@ public: template decltype(auto) patch(const entity_type entity, Func &&...func) { ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure()->patch(entity, std::forward(func)...); + return assure().patch(entity, std::forward(func)...); } /** @@ -686,7 +689,7 @@ public: */ template decltype(auto) replace(const entity_type entity, Args &&...args) { - return assure()->patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }); + return assure().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }); } /** @@ -703,7 +706,7 @@ public: size_type remove(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); - return (assure()->remove(entity) + ... + size_type{}); + return (assure().remove(entity) + ... + size_type{}); } /** @@ -720,13 +723,13 @@ public: template size_type remove(It first, It last) { static_assert(sizeof...(Component) > 0, "Provide one or more component types"); - const auto cpools = std::make_tuple(assure()...); + const auto cpools = std::forward_as_tuple(assure()...); size_type count{}; for(; first != last; ++first) { const auto entity = *first; ENTT_ASSERT(valid(entity), "Invalid entity"); - count += (std::get *>(cpools)->remove(entity) + ...); + count += (std::get &>(cpools).remove(entity) + ...); } return count; @@ -746,7 +749,7 @@ public: void erase(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); - (assure()->erase(entity), ...); + (assure().erase(entity), ...); } /** @@ -762,12 +765,12 @@ public: template void erase(It first, It last) { static_assert(sizeof...(Component) > 0, "Provide one or more component types"); - const auto cpools = std::make_tuple(assure()...); + const auto cpools = std::forward_as_tuple(assure()...); for(; first != last; ++first) { const auto entity = *first; ENTT_ASSERT(valid(entity), "Invalid entity"); - (std::get *>(cpools)->erase(entity), ...); + (std::get &>(cpools).erase(entity), ...); } } @@ -783,7 +786,7 @@ public: curr.second.pool->compact(); } } else { - (assure()->compact(), ...); + (assure().compact(), ...); } } @@ -800,7 +803,7 @@ public: template [[nodiscard]] bool all_of(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); - return [entity](const auto *...cpool) { return ((cpool && cpool->contains(entity)) && ...); }(pool_if_exists>()...); + return (assure>().contains(entity) && ...); } /** @@ -817,7 +820,7 @@ public: template [[nodiscard]] bool any_of(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); - return [entity](const auto *...cpool) { return !((!cpool || !cpool->contains(entity)) && ...); }(pool_if_exists>()...); + return (assure>().contains(entity) || ...); } /** @@ -836,9 +839,7 @@ public: ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr(sizeof...(Component) == 1) { - const auto *cpool = pool_if_exists...>(); - ENTT_ASSERT(cpool, "Storage not available"); - return cpool->get(entity); + return assure...>().get(entity); } else { return std::forward_as_tuple(get(entity)...); } @@ -847,10 +848,8 @@ public: /*! @copydoc get */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - if constexpr(sizeof...(Component) == 1) { - return (const_cast(assure>()->get(entity)), ...); + return (const_cast(assure>().get(entity)), ...); } else { return std::forward_as_tuple(get(entity)...); } @@ -874,8 +873,8 @@ public: template [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) { ENTT_ASSERT(valid(entity), "Invalid entity"); - auto *cpool = assure(); - return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(entity, std::forward(args)...); + auto &cpool = assure(); + return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward(args)...); } /** @@ -896,8 +895,8 @@ public: ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr(sizeof...(Component) == 1) { - const auto *cpool = pool_if_exists...>(); - return (cpool && cpool->contains(entity)) ? std::addressof(cpool->get(entity)) : nullptr; + const auto &cpool = assure...>(); + return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr; } else { return std::make_tuple(try_get(entity)...); } @@ -906,8 +905,6 @@ public: /*! @copydoc try_get */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - if constexpr(sizeof...(Component) == 1) { return (const_cast(std::as_const(*this).template try_get(entity)), ...); } else { @@ -928,7 +925,7 @@ public: each([this](const auto entity) { release_entity(entity, entity_traits::to_version(entity) + 1u); }); } else { - (assure()->clear(), ...); + (assure().clear(), ...); } } @@ -1021,7 +1018,7 @@ public: */ template [[nodiscard]] auto on_construct() { - return assure()->on_construct(); + return assure().on_construct(); } /** @@ -1044,7 +1041,7 @@ public: */ template [[nodiscard]] auto on_update() { - return assure()->on_update(); + return assure().on_update(); } /** @@ -1069,7 +1066,7 @@ public: */ template [[nodiscard]] auto on_destroy() { - return assure()->on_destroy(); + return assure().on_destroy(); } /** @@ -1107,14 +1104,14 @@ public: template [[nodiscard]] basic_view...>, exclude_t> view(exclude_t = {}) const { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); - return {*assure>()..., *assure()...}; + return {assure>()..., assure()...}; } /*! @copydoc view */ template [[nodiscard]] basic_view, exclude_t> view(exclude_t = {}) { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); - return {*assure>()..., *assure()...}; + return {assure>()..., assure()...}; } /** @@ -1200,7 +1197,7 @@ public: using handler_type = group_handler, get_t...>, std::remove_const_t...>; - const auto cpools = std::make_tuple(assure>()..., assure>()...); + const auto cpools = std::forward_as_tuple(assure>()..., assure>()...); constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); handler_type *handler = nullptr; @@ -1265,13 +1262,13 @@ public: } } else { // we cannot iterate backwards because we want to leave behind valid entities in case of owned types - for(auto *first = std::get<0>(cpools)->data(), *last = first + std::get<0>(cpools)->size(); first != last; ++first) { + for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) { handler->template maybe_valid_if...>>>(*this, *first); } } } - return {handler->current, *std::get> *>(cpools)..., *std::get> *>(cpools)...}; + return {handler->current, std::get> &>(cpools)..., std::get> &>(cpools)...}; } /*! @copydoc group */ @@ -1288,7 +1285,7 @@ public: return {}; } else { using handler_type = group_handler, get_t...>, std::remove_const_t...>; - return {static_cast(it->group.get())->current, *pool_if_exists>()..., *pool_if_exists>()...}; + return {static_cast(it->group.get())->current, assure>()..., assure>()...}; } } @@ -1370,13 +1367,13 @@ public: template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { ENTT_ASSERT(sortable(), "Cannot sort owned storage"); - auto *cpool = assure(); + auto &cpool = assure(); - if constexpr(std::is_invocable_vget({})), decltype(cpool->get({}))>) { - auto comp = [cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool->get(lhs)), std::as_const(cpool->get(rhs))); }; - cpool->sort(std::move(comp), std::move(algo), std::forward(args)...); + if constexpr(std::is_invocable_v) { + auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; + cpool.sort(std::move(comp), std::move(algo), std::forward(args)...); } else { - cpool->sort(std::move(compare), std::move(algo), std::forward(args)...); + cpool.sort(std::move(compare), std::move(algo), std::forward(args)...); } } @@ -1407,7 +1404,7 @@ public: template void sort() { ENTT_ASSERT(sortable(), "Cannot sort owned storage"); - assure()->respect(*assure()); + assure().respect(assure()); } /** @@ -1576,7 +1573,7 @@ public: } private: - mutable dense_hash_map pools{}; + dense_hash_map pools{}; dense_hash_map, identity> vars{}; std::vector groups{}; std::vector entities{}; diff --git a/test/entt/entity/view.cpp b/test/entt/entity/view.cpp index 6169fd0e3..1e8756bba 100644 --- a/test/entt/entity/view.cpp +++ b/test/entt/entity/view.cpp @@ -135,18 +135,18 @@ TEST(SingleComponentView, LazyTypeFromConstRegistry) { ASSERT_TRUE(cview); ASSERT_TRUE(eview); - ASSERT_NE(cview.raw(), nullptr); - ASSERT_NE(eview.data(), nullptr); + ASSERT_EQ(cview.raw(), nullptr); + ASSERT_EQ(eview.data(), nullptr); - ASSERT_FALSE(cview.empty()); - ASSERT_EQ(eview.size(), 1u); - ASSERT_TRUE(cview.contains(entity)); + ASSERT_TRUE(cview.empty()); + ASSERT_EQ(eview.size(), 0u); + ASSERT_FALSE(cview.contains(entity)); - ASSERT_NE(cview.begin(), cview.end()); - ASSERT_NE(eview.rbegin(), eview.rend()); - ASSERT_NE(eview.find(entity), eview.end()); - ASSERT_EQ(cview.front(), entity); - ASSERT_EQ(eview.back(), entity); + ASSERT_EQ(cview.begin(), cview.end()); + ASSERT_EQ(eview.rbegin(), eview.rend()); + ASSERT_EQ(eview.find(entity), eview.end()); + ASSERT_NE(cview.front(), entity); + ASSERT_NE(eview.back(), entity); } TEST(SingleComponentView, ElementAccess) { @@ -572,13 +572,13 @@ TEST(MultiComponentView, LazyTypesFromConstRegistry) { ASSERT_TRUE(view); - ASSERT_EQ(view.size_hint(), 1u); - ASSERT_TRUE(view.contains(entity)); + ASSERT_EQ(view.size_hint(), 0u); + ASSERT_FALSE(view.contains(entity)); - ASSERT_NE(view.begin(), view.end()); - ASSERT_NE(view.find(entity), view.end()); - ASSERT_EQ(view.front(), entity); - ASSERT_EQ(view.back(), entity); + ASSERT_EQ(view.begin(), view.end()); + ASSERT_EQ(view.find(entity), view.end()); + ASSERT_NE(view.front(), entity); + ASSERT_NE(view.back(), entity); } TEST(MultiComponentView, LazyExcludedTypeFromConstRegistry) {