From d0deefd0d7fcd26733bd8343bd100928fa60acf0 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Fri, 8 Mar 2019 23:54:22 +0100 Subject: [PATCH] review: registry (fix #199) --- src/entt/entity/registry.hpp | 202 +++++++++++++++++++---------------- test/entt/entity/helper.cpp | 13 +++ 2 files changed, 120 insertions(+), 95 deletions(-) diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 7d2edef46..8ce40386d 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -2,7 +2,6 @@ #define ENTT_ENTITY_REGISTRY_HPP -#include #include #include #include @@ -64,7 +63,39 @@ class registry { using traits_type = entt_traits; template - using pool_type = sparse_set>; + struct pool_wrapper: sparse_set { + template + Component & construct(Entity entity, Args &&... args) { + auto &component = sparse_set::construct(entity, std::forward(args)...); + construction.publish(*owner, entity); + return component; + } + + template + Component * construct(It first, It last, typename sparse_set::size_type hint) { + auto *component = sparse_set::construct(first, last, hint); + + if(!construction.empty()) { + std::for_each(first, last, [this](const auto entity) { + construction.publish(*owner, entity); + }); + } + + return component; + } + + void destroy(Entity entity) { + destruction.publish(*owner, entity); + sparse_set::destroy(entity); + } + + signal_type construction; + signal_type destruction; + registry *owner; + }; + + template + using pool_type = pool_wrapper>; template struct non_owning_group; @@ -98,27 +129,25 @@ class registry { void induce_if(registry ®, const Entity entity) { if(reg.has(entity) && (0 + ... + reg.has(entity)) == Accepted) { const auto curr = this->owned++; - auto pools = std::make_tuple(std::get<1>(reg.pool())...); - (std::swap(std::get *>(pools)->get(entity), std::get *>(pools)->raw()[curr]), ...); - (std::get *>(pools)->swap(std::get *>(pools)->sparse_set::get(entity), curr), ...); + const auto cpools = std::make_tuple(reg.pool()...); + (std::swap(std::get *>(cpools)->get(entity), std::get *>(cpools)->raw()[curr]), ...); + (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entity), curr), ...); } } void discard_if(registry ®, const Entity entity) { - auto pools = std::make_tuple(std::get<1>(reg.pool())...); + const auto cpools = std::make_tuple(reg.pool()...); - if(std::get<0>(pools)->has(entity) && std::get<0>(pools)->sparse_set::get(entity) < this->owned) { + if(std::get<0>(cpools)->has(entity) && std::get<0>(cpools)->sparse_set::get(entity) < this->owned) { const auto curr = --this->owned; - (std::swap(std::get *>(pools)->get(entity), std::get *>(pools)->raw()[curr]), ...); - (std::get *>(pools)->swap(std::get *>(pools)->sparse_set::get(entity), curr), ...); + (std::swap(std::get *>(cpools)->get(entity), std::get *>(cpools)->raw()[curr]), ...); + (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entity), curr), ...); } } }; struct pool_data { std::unique_ptr> pool; - signal_type construction; - signal_type destruction; ENTT_ID_TYPE runtime_type; }; @@ -150,7 +179,7 @@ class registry { } template - inline auto pool() const ENTT_NOEXCEPT { + inline const auto * pool() const ENTT_NOEXCEPT { const auto ctype = type(); if constexpr(is_shared_v) { @@ -158,16 +187,23 @@ class registry { return pdata.pool && pdata.runtime_type == ctype; }); - assert(it != pools.cend() && it->pool); - return std::make_tuple(&*it, static_cast *>(it->pool.get())); + return (it != pools.cend() && it->pool) + ? static_cast *>(it->pool.get()) + : nullptr; } else { - assert(ctype < pools.size() && pools[ctype].pool && pools[ctype].runtime_type == ctype); - return std::make_tuple(&pools[ctype], static_cast *>(pools[ctype].pool.get())); + return (ctype < pools.size() && pools[ctype].pool && pools[ctype].runtime_type == ctype) + ? static_cast *>(pools[ctype].pool.get()) + : nullptr; } } template - auto assure() const { + inline auto * pool() ENTT_NOEXCEPT { + return const_cast *>(std::as_const(*this).template pool()); + } + + template + auto * assure() { const auto ctype = type(); pool_data *pdata = nullptr; @@ -193,10 +229,11 @@ class registry { if(!pdata->pool) { pdata->pool = std::make_unique>(); + static_cast &>(*pdata->pool).owner = this; pdata->runtime_type = ctype; } - return std::make_tuple(pdata, static_cast *>(pdata->pool.get())); + return static_cast *>(pdata->pool.get()); } public: @@ -247,7 +284,8 @@ public: */ template size_type size() const ENTT_NOEXCEPT { - return std::get<1>(assure())->size(); + const auto *cpool = pool(); + return cpool ? cpool->size() : size_type{}; } /** @@ -277,7 +315,7 @@ public: */ template void reserve(const size_type cap) { - std::get<1>(assure())->reserve(cap); + assure()->reserve(cap); } /** @@ -299,7 +337,8 @@ public: */ template size_type capacity() const ENTT_NOEXCEPT { - return std::get<1>(assure())->capacity(); + const auto *cpool = pool(); + return cpool ? cpool->capacity() : size_type{}; } /** @@ -319,7 +358,8 @@ public: */ template bool empty() const ENTT_NOEXCEPT { - return std::get<1>(assure())->empty(); + const auto *cpool = pool(); + return cpool ? cpool->empty() : true; } /** @@ -349,8 +389,9 @@ public: * @return A pointer to the array of components of the given type. */ template - std::add_const_t * raw() const ENTT_NOEXCEPT { - return std::get<1>(assure())->raw(); + const Component * raw() const ENTT_NOEXCEPT { + const auto *cpool = pool(); + return cpool ? cpool->raw() : nullptr; } /*! @copydoc raw */ @@ -375,7 +416,8 @@ public: */ template const entity_type * data() const ENTT_NOEXCEPT { - return std::get<1>(assure())->data(); + const auto *cpool = pool(); + return cpool ? cpool->data() : nullptr; } /** @@ -554,21 +596,7 @@ public: if constexpr(sizeof...(Component) > 0) { const auto hint = size_type(std::max(candidate, *(last-1)))+1; - - auto generator = [first, last, hint, this](auto &&adata) { - auto *comp = std::get<1>(adata)->construct(first, last, hint); - auto *pdata = std::get<0>(adata); - - if(!pdata->construction.empty()) { - std::for_each(first, last, [pdata, this](const auto entity) { - pdata->construction.publish(*this, entity); - }); - } - - return comp; - }; - - return { generator(assure())... }; + return { assure()->construct(first, last, hint)... }; } } @@ -601,7 +629,6 @@ public: auto &pdata = pools[pos-1]; if(pdata.pool && pdata.pool->has(entity)) { - pdata.destruction.publish(*this, entity); pdata.pool->destroy(entity); } }; @@ -627,7 +654,6 @@ public: if(pdata.pool) { std::for_each(first, last, [&pdata, this](const auto entity) { if(pdata.pool->has(entity)) { - pdata.destruction.publish(*this, entity); pdata.pool->destroy(entity); } }); @@ -665,10 +691,7 @@ public: template Component & assign(const entity_type entity, Args &&... args) { assert(valid(entity)); - auto [pdata, cpool] = assure(); - auto &component = cpool->construct(entity, std::forward(args)...); - pdata->construction.publish(*this, entity); - return component; + return assure()->construct(entity, std::forward(args)...); } /** @@ -687,9 +710,7 @@ public: template void remove(const entity_type entity) { assert(valid(entity)); - auto [pdata, cpool] = pool(); - pdata->destruction.publish(*this, entity); - cpool->destroy(entity); + pool()->destroy(entity); } /** @@ -707,7 +728,10 @@ public: template bool has(const entity_type entity) const ENTT_NOEXCEPT { assert(valid(entity)); - return (std::get<1>(assure())->has(entity) && ...); + [[maybe_unused]] const auto cpools = std::make_tuple(pool()...); + return ((std::get *>(cpools) + ? std::get *>(cpools)->has(entity) + : false) && ...); } /** @@ -729,7 +753,7 @@ public: assert(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (std::as_const(*std::get<1>(pool())).get(entity), ...); + return (pool()->get(entity), ...); } else { return std::tuple &...>{get(entity)...}; } @@ -771,15 +795,9 @@ public: template Component & get(const entity_type entity, Component &&component) ENTT_NOEXCEPT { assert(valid(entity)); - auto [pdata, cpool] = assure>(); + auto *cpool = assure>(); auto *comp = cpool->try_get(entity); - - if(!comp) { - comp = &cpool->construct(entity, std::forward(component)); - pdata->construction.publish(*this, entity); - } - - return *comp; + return comp ? *comp : cpool->construct(entity, std::forward(component)); } /** @@ -799,7 +817,10 @@ public: assert(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (std::as_const(*std::get<1>(assure())).try_get(entity), ...); + const auto cpools = std::make_tuple(pool()...); + return ((std::get *>(cpools) + ? std::get *>(cpools)->try_get(entity) + : nullptr), ...); } else { return std::tuple *...>{try_get(entity)...}; } @@ -837,7 +858,7 @@ public: */ template Component & replace(const entity_type entity, Args &&... args) { - return (std::get<1>(pool())->get(entity) = std::decay_t{std::forward(args)...}); + return (pool()->get(entity) = std::decay_t{std::forward(args)...}); } /** @@ -868,14 +889,13 @@ public: */ template Component & assign_or_replace(const entity_type entity, Args &&... args) { - auto [pdata, cpool] = assure(); + auto *cpool = assure(); auto *comp = cpool->try_get(entity); if(comp) { *comp = std::decay_t{std::forward(args)...}; } else { comp = &cpool->construct(entity, std::forward(args)...); - pdata->construction.publish(*this, entity); } return *comp; @@ -906,7 +926,7 @@ public: */ template sink_type construction() ENTT_NOEXCEPT { - return std::get<0>(assure())->construction.sink(); + return assure()->construction.sink(); } /** @@ -934,7 +954,7 @@ public: */ template sink_type destruction() ENTT_NOEXCEPT { - return std::get<0>(assure())->destruction.sink(); + return assure()->destruction.sink(); } /** @@ -988,7 +1008,7 @@ public: template void sort(Compare compare, Sort sort = Sort{}, Args &&... args) { assert(!owned()); - std::get<1>(assure())->sort(std::move(compare), std::move(sort), std::forward(args)...); + assure()->sort(std::move(compare), std::move(sort), std::forward(args)...); } /** @@ -1031,7 +1051,7 @@ public: template void sort() { assert(!owned()); - std::get<1>(assure())->respect(*std::get<1>(assure())); + assure()->respect(*assure()); } /** @@ -1051,10 +1071,9 @@ public: template void reset(const entity_type entity) { assert(valid(entity)); - auto [pdata, cpool] = assure(); + auto *cpool = assure(); if(cpool->has(entity)) { - pdata->destruction.publish(*this, entity); cpool->destroy(entity); } } @@ -1069,16 +1088,15 @@ public: */ template void reset() { - auto [pdata, cpool] = assure(); + auto *cpool = assure(); - if(pdata->destruction.empty()) { + if(cpool->destruction.empty()) { // no group set, otherwise the signal wouldn't be empty cpool->reset(); } else { const sparse_set &base = *cpool; for(const auto entity: base) { - pdata->destruction.publish(*this, entity); cpool->destroy(entity); } } @@ -1220,7 +1238,7 @@ public: */ template entt::view view() { - return { std::get<1>(assure())... }; + return { assure()... }; } /*! @copydoc view */ @@ -1291,16 +1309,13 @@ public: gdata.extent = sizeof...(Get) + sizeof...(Exclude); auto *curr = static_cast(gdata.data.get()); - pool_data *pdata; + const auto cpools = std::make_tuple(assure()..., assure()...); - ((pdata = std::get<0>(assure()), - pdata->destruction.sink().template connect<&group_type::destroy_if>(curr), - pdata->construction.sink().template connect<&group_type::template construct_if<0>>(curr)), - ...); + (std::get *>(cpools)->destruction.sink().template connect<&group_type::destroy_if>(curr), ...); + (std::get *>(cpools)->construction.sink().template connect<&group_type::template construct_if<0>>(curr), ...); - ((pdata = std::get<0>(assure()), - pdata->destruction.sink().template connect<&group_type::template construct_if<1>>(curr), - pdata->construction.sink().template connect<&group_type::destroy_if>(curr)), ...); + (std::get *>(cpools)->destruction.sink().template connect<&group_type::template construct_if<1>>(curr), ...); + (std::get *>(cpools)->construction.sink().template connect<&group_type::destroy_if>(curr), ...); for(const auto entity: view()) { if(!(has(entity) || ...)) { @@ -1311,7 +1326,7 @@ public: it = std::prev(outer_groups.end()); } - return { it->data.get(), std::get<1>(pool())... }; + return { it->data.get(), pool()... }; } else { auto it = std::find_if(inner_groups.begin(), inner_groups.end(), [](auto &&gdata) { return gdata.extent == sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) @@ -1332,21 +1347,18 @@ public: gdata.extent = sizeof...(Get) + sizeof...(Exclude) + sizeof...(Owned); auto *curr = static_cast(gdata.data.get()); - pool_data *pdata; + const auto cpools = std::make_tuple(assure()..., assure()..., assure()...); - ((pdata = std::get<0>(assure()), - pdata->construction.sink().template connect<&group_type::template induce_if<0>>(curr), - pdata->destruction.sink().template connect<&group_type::discard_if>(curr)), ...); + (std::get *>(cpools)->construction.sink().template connect<&group_type::template induce_if<0>>(curr), ...); + (std::get *>(cpools)->destruction.sink().template connect<&group_type::discard_if>(curr), ...); - ((pdata = std::get<0>(assure()), - pdata->destruction.sink().template connect<&group_type::template induce_if<1>>(curr), - pdata->construction.sink().template connect<&group_type::discard_if>(curr)), ...); + (std::get *>(cpools)->construction.sink().template connect<&group_type::template induce_if<0>>(curr), ...); + (std::get *>(cpools)->destruction.sink().template connect<&group_type::discard_if>(curr), ...); - auto candidate = {(pdata = std::get<0>(assure()), - pdata->construction.sink().template connect<&group_type::template induce_if<0>>(curr), - pdata->destruction.sink().template connect<&group_type::discard_if>(curr), pdata->pool.get())...}; + (std::get *>(cpools)->destruction.sink().template connect<&group_type::template induce_if<1>>(curr), ...); + (std::get *>(cpools)->construction.sink().template connect<&group_type::discard_if>(curr), ...); - const auto *cpool = std::min(candidate, [](const auto *lhs, const auto *rhs) { + const auto *cpool = std::min({ static_cast *>(std::get *>(cpools))... }, [](const auto *lhs, const auto *rhs) { return lhs->size() < rhs->size(); }); @@ -1358,7 +1370,7 @@ public: it = std::prev(inner_groups.end()); } - return { &it->data->owned, std::get<1>(pool())..., std::get<1>(pool())... }; + return { &it->data->owned, pool()..., pool()... }; } } @@ -1537,7 +1549,7 @@ public: } private: - mutable std::vector pools; + std::vector pools; std::vector inner_groups; std::vector outer_groups; std::vector entities; diff --git a/test/entt/entity/helper.cpp b/test/entt/entity/helper.cpp index e3a519f63..3496db598 100644 --- a/test/entt/entity/helper.cpp +++ b/test/entt/entity/helper.cpp @@ -2,6 +2,7 @@ #include #include #include +#include TEST(Helper, AsView) { using entity_type = typename entt::registry<>::entity_type; @@ -71,6 +72,18 @@ TEST(Helper, Dependency) { ASSERT_FALSE(registry.has(entity)); } +TEST(Dependency, MultipleListenersOnTheSameType) { + entt::registry<> registry; + entt::connect(registry.construction()); + entt::connect(registry.construction()); + + const auto entity = registry.create(); + registry.assign(entity); + + ASSERT_TRUE(registry.has(entity)); + ASSERT_TRUE(registry.has(entity)); +} + TEST(Helper, Label) { entt::registry<> registry; const auto entity = registry.create();