Browse Source

lib support for registry (first draft)

Michele Caini 7 years ago
parent
commit
e0fd83211b

+ 2 - 5
TODO

@@ -19,14 +19,11 @@
 * provide create with a pack of default constructible components to assign
 * allow to replace std:: with custom implementations
 * allow to sort groups (::respect can already work with begin/end instead of a whole sparse set)
-<<<<<<< HEAD
-=======
+* README file: add section with contacts (gitter and the others)
+* cleanup - see https://github.com/skypjack/entt/commit/ad5cedc08c83e8cbcc8aaeac9634d44624ffe35a#commitcomment-32380903
 
 ==> can we do more for shared libraries? who knows... see #144
-- use stable component identifiers independent from families and hashed strings
-* to be updated: runtime view (also in doc, no longer missing pools)
 * to be updated: dispatcher
 * to be updated: registry
 * to be updated: emitter
 * to be updated: doc
->>>>>>> experimental

+ 1 - 1
src/entt/core/type_traits.hpp

@@ -100,7 +100,7 @@ constexpr auto is_shared_v = is_shared<Type>::value;
     template<>\
     struct entt::shared_traits<type>\
         : std::integral_constant<typename entt::hashed_string::hash_type, entt::hashed_string::to_value(#type)>\
-    {}
+    {};
 
 
 /**

+ 2 - 2
src/entt/entity/group.hpp

@@ -351,7 +351,7 @@ class group<Entity, get_t<Get...>, Owned...> {
     using component_iterator_type = decltype(std::declval<pool_type<Component>>().begin());
 
     // we could use pool_type<Type> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
-    group(typename sparse_set<Entity>::size_type *length, sparse_set<Entity, std::remove_const_t<Owned>> *... owned, sparse_set<Entity, std::remove_const_t<Get>> *... others) ENTT_NOEXCEPT
+    group(const typename registry<Entity>::size_type *length, sparse_set<Entity, std::remove_const_t<Owned>> *... owned, sparse_set<Entity, std::remove_const_t<Get>> *... others) ENTT_NOEXCEPT
         : length{length},
           pools{owned..., others...}
     {}
@@ -555,7 +555,7 @@ public:
     }
 
 private:
-    const size_type *length;
+    const typename registry<Entity>::size_type *length;
     const std::tuple<pool_type<Owned> *..., pool_type<Get> *...> pools;
 };
 

+ 133 - 98
src/entt/entity/registry.hpp

@@ -60,85 +60,83 @@ constexpr exclude_t<Type...> exclude{};
 template<typename Entity = std::uint32_t>
 class registry {
     using component_family = family<struct internal_registry_component_family>;
-    using group_family = family<struct internal_registry_group_family>;
     using signal_type = sigh<void(registry &, const Entity)>;
     using traits_type = entt_traits<Entity>;
 
-    struct pool_data {
-        std::unique_ptr<sparse_set<Entity>> pool;
-        signal_type construction;
-        signal_type destruction;
-        ENTT_ID_TYPE runtime_type;
+    template<typename, typename>
+    struct non_owning_group;
+
+    template<typename... Get, typename... Exclude>
+    struct non_owning_group<type_list<Exclude...>, type_list<Get...>>: sparse_set<Entity> {
+        template<auto Accepted>
+        void construct_if(registry &reg, const Entity entity) {
+            if(reg.has<Get...>(entity) && (0 + ... + reg.has<Exclude>(entity)) == Accepted) {
+                this->construct(entity);
+            }
+        }
+
+        void destroy_if(registry &, const Entity entity) {
+            if(has(entity)) {
+                destroy(entity);
+            }
+        }
     };
 
-    struct basic_descriptor {
-        virtual ~basic_descriptor() = default;
-        virtual bool owns(typename component_family::family_type) = 0;
+    struct boxed_owned {
+        std::size_t owned;
     };
 
     template<typename...>
-    struct descriptor;
-
-    template<typename Ref, typename... Owned, typename... Get, typename... Exclude>
-    struct descriptor<exclude_t<Exclude...>, get_t<Get...>, Ref, Owned...>: basic_descriptor {
-        typename sparse_set<Entity>::size_type data;
+    struct owning_group;
 
+    template<typename... Owned, typename... Get, typename... Exclude>
+    struct owning_group<type_list<Exclude...>, type_list<Get...>, Owned...>: boxed_owned {
         template<auto Accepted>
         void induce_if(registry &reg, const Entity entity) {
-            if(reg.has<Ref, Owned..., Get...>(entity) && (0 + ... + reg.has<Exclude>(entity)) == Accepted) {
-                auto upd = [entity, curr = data++](auto &&cpool) {
-                    std::swap(cpool.get(entity), cpool.raw()[curr]);
-                    cpool.swap(cpool.sparse_set<Entity>::get(entity), curr);
-                };
-
-                upd(reg.pool<Ref>());
-                (upd(reg.pool<Owned>()), ...);
-
+            if(reg.has<Owned..., Get...>(entity) && (0 + ... + reg.has<Exclude>(entity)) == Accepted) {
+                const auto curr = this->owned++;
+                auto pools = std::make_tuple(&reg.pool<Owned>()...);
+                (std::swap(std::get<sparse_set<Entity, Owned> *>(pools)->get(entity), std::get<sparse_set<Entity, Owned> *>(pools)->raw()[curr]), ...);
+                (std::get<sparse_set<Entity, Owned> *>(pools)->swap(std::get<sparse_set<Entity, Owned> *>(pools)->sparse_set<Entity>::get(entity), curr), ...);
             }
         }
 
         void discard_if(registry &reg, const Entity entity) {
-            auto &cpool = reg.pool<Ref>();
+            auto pools = std::make_tuple(&reg.pool<Owned>()...);
 
-            if(cpool.has(entity) && cpool.sparse_set<Entity>::get(entity) < data) {
-                auto upd = [entity, curr = --data](auto &&cpool) {
-                    std::swap(cpool.get(entity), cpool.raw()[curr]);
-                    cpool.swap(cpool.sparse_set<Entity>::get(entity), curr);
-                };
-
-                upd(cpool);
-                (upd(reg.pool<Owned>()), ...);
+            if(std::get<0>(pools)->has(entity) && std::get<0>(pools)->sparse_set<Entity>::get(entity) < this->owned) {
+                const auto curr = --this->owned;
+                (std::swap(std::get<sparse_set<Entity, Owned> *>(pools)->get(entity), std::get<sparse_set<Entity, Owned> *>(pools)->raw()[curr]), ...);
+                (std::get<sparse_set<Entity, Owned> *>(pools)->swap(std::get<sparse_set<Entity, Owned> *>(pools)->sparse_set<Entity>::get(entity), curr), ...);
             }
         }
-
-        bool owns(typename component_family::family_type ctype) override {
-            return ((ctype == type<Ref>()) || ... || (ctype == type<Owned>()));
-        }
     };
 
-    template<typename... Get, typename... Exclude>
-    struct descriptor<exclude_t<Exclude...>, get_t<Get...>>: basic_descriptor {
-        sparse_set<Entity> data;
-
-        template<auto Accepted>
-        void construct_if(registry &reg, const Entity entity) {
-            if(reg.has<Get...>(entity) && (0 + ... + reg.has<Exclude>(entity)) == Accepted) {
-                data.construct(entity);
-            }
-        }
+    struct pool_data {
+        std::unique_ptr<sparse_set<Entity>> pool;
+        signal_type construction;
+        signal_type destruction;
+        ENTT_ID_TYPE runtime_type;
+    };
 
-        void destroy_if(registry &, const Entity entity) {
-            if(data.has(entity)) {
-                data.destroy(entity);
-            }
-        }
+    struct owning_group_data {
+        using match_fn_type = bool(ENTT_ID_TYPE);
+        std::unique_ptr<boxed_owned> data;
+        match_fn_type *exclude;
+        match_fn_type *get;
+        match_fn_type *owned;
+        std::size_t extent;
+    };
 
-        bool owns(typename component_family::family_type) override {
-            return false;
-        }
+    struct non_owning_group_data {
+        using match_fn_type = bool(ENTT_ID_TYPE);
+        std::unique_ptr<sparse_set<Entity>> data;
+        match_fn_type *exclude;
+        match_fn_type *get;
+        std::size_t extent;
     };
 
-    inline auto get_pool_data(typename component_family::family_type ctype) const ENTT_NOEXCEPT {
+    inline auto get_pool_data(ENTT_ID_TYPE ctype) const ENTT_NOEXCEPT {
         return std::find_if(pools.begin(), pools.end(), [ctype](const auto &pdata) {
             return pdata.pool && pdata.runtime_type == ctype;
         });
@@ -149,7 +147,7 @@ class registry {
         if constexpr(is_shared_v<Component>) {
             const auto it = get_pool_data(type<Component>());
             assert(it != pools.cend() && it->pool);
-            return static_cast<const sparse_set<Entity, std::decay_t<Component>> &>(it->pool);
+            return static_cast<const sparse_set<Entity, std::decay_t<Component>> &>(*it->pool);
         } else {
             const auto ctype = type<Component>();
             assert(ctype < pools.size() && pools[ctype].pool && pools[ctype].runtime_type == ctype);
@@ -205,7 +203,7 @@ public:
     /*! @brief Unsigned integer type. */
     using size_type = typename sparse_set<Entity>::size_type;
     /*! @brief Unsigned integer type. */
-    using component_type = typename component_family::family_type;
+    using component_type = ENTT_ID_TYPE;
     /*! @brief Type of sink for the given component. */
     using sink_type = typename signal_type::sink_type;
 
@@ -1192,8 +1190,8 @@ public:
      */
     template<typename Component>
     bool owned() const ENTT_NOEXCEPT {
-        return std::any_of(groups.cbegin(), groups.cend(), [this](const auto &curr) {
-            return curr && curr->owns(type<Component>());
+        return std::any_of(inner_groups.cbegin(), inner_groups.cend(), [](auto &&gdata) {
+            return gdata.owned(type<Component>());
         });
     }
 
@@ -1229,60 +1227,96 @@ public:
         static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1);
         static_assert(sizeof...(Owned) + sizeof...(Get));
 
-        using descriptor_type = descriptor<exclude_t<std::decay_t<Exclude>...>, get_t<std::decay_t<Get>...>, std::decay_t<Owned>...>;
-        const auto gtype = group_family::type<exclude_t<std::decay_t<Exclude>...>, get_t<std::decay_t<Get>...>, std::decay_t<Owned>...>;
-
         (assure<Owned>(), ...);
         (assure<Get>(), ...);
         (assure<Exclude>(), ...);
 
-        if(!(gtype < groups.size())) {
-            groups.resize(gtype + 1);
-        }
+        if constexpr(sizeof...(Owned) == 0) {
+            auto it = std::find_if(outer_groups.begin(), outer_groups.end(), [](auto &&gdata) {
+                return gdata.extent == sizeof...(Get) + sizeof...(Exclude)
+                        && (gdata.exclude(type<Exclude>()) && ...)
+                        && (gdata.get(type<Get>()) && ...);
+            });
+
+            if(it == outer_groups.cend()) {
+                using group_type = non_owning_group<type_list<std::decay_t<Exclude>...>, type_list<std::decay_t<Get>...>>;
+                auto &gdata = outer_groups.emplace_back();
+
+                gdata.data = std::make_unique<group_type>();
+                gdata.exclude = +[](component_type ctype) { return ((ctype == type<Exclude>()) || ...); };
+                gdata.get = +[](component_type ctype) { return ((ctype == type<Get>()) || ...); };
+                gdata.extent = sizeof...(Get) + sizeof...(Exclude);
 
-        if(!groups[gtype]) {
-            assert((!owned<Owned>() && ...));
-            groups[gtype] = std::make_unique<descriptor_type>();
-            auto *curr = static_cast<descriptor_type *>(groups[gtype].get());
-            decltype(get_pool_data({})) it;
+                auto *curr = static_cast<group_type *>(gdata.data.get());
+                decltype(get_pool_data({})) pdata;
 
-            if constexpr(sizeof...(Owned) == 0) {
-                ((it = get_pool_data(type<Get>()),
-                        it->destruction.sink().template connect<&descriptor_type::destroy_if>(curr),
-                        it->construction.sink().template connect<&descriptor_type::template construct_if<0>>(curr)),
+                ((pdata = get_pool_data(type<Get>()),
+                        pdata->destruction.sink().template connect<&group_type::destroy_if>(curr),
+                        pdata->construction.sink().template connect<&group_type::template construct_if<0>>(curr)),
                         ...);
 
-                ((it = get_pool_data(type<Exclude>()),
-                        it->destruction.sink().template connect<&descriptor_type::template construct_if<1>>(curr),
-                        it->construction.sink().template connect<&descriptor_type::destroy_if>(curr)), ...);
+                ((pdata = get_pool_data(type<Exclude>()),
+                        pdata->destruction.sink().template connect<&group_type::template construct_if<1>>(curr),
+                        pdata->construction.sink().template connect<&group_type::destroy_if>(curr)), ...);
 
                 for(const auto entity: view<Get...>()) {
-                    curr->template construct_if<0>(*this, entity);
+                    if(!(has<Exclude>(entity) || ...)) {
+                        curr->construct(entity);
+                    }
                 }
-            } else {
-                ((it = get_pool_data(type<Get>()),
-                        it->construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr),
-                        it->destruction.sink().template connect<&descriptor_type::discard_if>(curr)), ...);
 
-                ((it = get_pool_data(type<Exclude>()),
-                        it->destruction.sink().template connect<&descriptor_type::template induce_if<1>>(curr),
-                        it->construction.sink().template connect<&descriptor_type::discard_if>(curr)), ...);
-
-                auto owned = {((it = get_pool_data(type<Owned>()),
-                        it->construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr),
-                        it->destruction.sink().template connect<&descriptor_type::discard_if>(curr), it->pool.get()), ...)};
+                it = std::prev(outer_groups.end());
+            }
 
-                const auto *cpool = std::min(owned, [](const auto *lhs, const auto *rhs) {
+            return { it->data.get(), &pool<Owned>()..., &pool<Get>()... };
+        } else {
+            auto it = std::find_if(inner_groups.begin(), inner_groups.end(), [](auto &&gdata) {
+                return gdata.extent == sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude)
+                        && (gdata.exclude(type<Exclude>()) && ...)
+                        && (gdata.get(type<Get>()) && ...)
+                        && (gdata.owned(type<Owned>()) && ...);
+            });
+
+            if(it == inner_groups.cend()) {
+                assert(!(owned<Owned>() || ...));
+                using group_type = owning_group<type_list<std::decay_t<Exclude>...>, type_list<std::decay_t<Get>...>, std::decay_t<Owned>...>;
+                auto &gdata = inner_groups.emplace_back();
+
+                gdata.data = std::make_unique<group_type>();
+                gdata.exclude = +[](component_type ctype) { return ((ctype == type<Exclude>()) || ...); };
+                gdata.get = +[](component_type ctype) { return ((ctype == type<Get>()) || ...); };
+                gdata.owned = +[](component_type ctype) { return ((ctype == type<Owned>()) || ...); };
+                gdata.extent = sizeof...(Get) + sizeof...(Exclude) + sizeof...(Owned);
+
+                auto *curr = static_cast<group_type *>(gdata.data.get());
+                decltype(get_pool_data({})) pdata;
+
+                ((pdata = get_pool_data(type<Get>()),
+                        pdata->construction.sink().template connect<&group_type::template induce_if<0>>(curr),
+                        pdata->destruction.sink().template connect<&group_type::discard_if>(curr)), ...);
+
+                ((pdata = get_pool_data(type<Exclude>()),
+                        pdata->destruction.sink().template connect<&group_type::template induce_if<1>>(curr),
+                        pdata->construction.sink().template connect<&group_type::discard_if>(curr)), ...);
+
+                auto candidate = {(pdata = get_pool_data(type<Owned>()),
+                        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())...};
+
+                const auto *cpool = std::min(candidate, [](const auto *lhs, const auto *rhs) {
                     return lhs->size() < rhs->size();
                 });
 
+                // we cannot iterate backwards because we want to leave behind valid entities
                 std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, this](const auto entity) {
                     curr->template induce_if<0>(*this, entity);
                 });
+
+                it = std::prev(inner_groups.end());
             }
-        }
 
-        return { &static_cast<descriptor_type &>(*groups[gtype]).data, &pool<Owned>()..., &pool<Get>()... };
+            return { &it->data->owned, &pool<Owned>()..., &pool<Get>()... };
+        }
     }
 
     /*! @copydoc group */
@@ -1314,7 +1348,7 @@ public:
      * do not require any type of initialization.<br/>
      * As a rule of thumb, storing a view should never be an option.
      *
-     * Runtime views are well suited when users want to construct a view from
+     * Runtime views are to be used when users want to construct a view from
      * some external inputs and don't know at compile-time what are the required
      * components.<br/>
      * This is particularly well suited to plugin systems and mods in general.
@@ -1357,10 +1391,10 @@ public:
      * @warning
      * Attempting to clone components that aren't copyable can result in
      * unexpected behaviors.<br/>
-     * A static assertion will abort the compilation when one or more components
-     * are provided at the call site. Otherwise, an assertion will abort the
-     * execution at runtime in debug mode in case one or more pools cannot be
-     * cloned.
+     * A static assertion will abort the compilation when the components
+     * provided aren't copy constructible. Otherwise, an assertion will abort
+     * the execution at runtime in debug mode in case one or more pools cannot
+     * be cloned.
      *
      * @tparam Component Types of components to clone.
      * @return A fresh copy of the registry.
@@ -1457,8 +1491,9 @@ public:
     }
 
 private:
-    std::vector<std::unique_ptr<basic_descriptor>> groups;
     mutable std::vector<pool_data> pools;
+    std::vector<owning_group_data> inner_groups;
+    std::vector<non_owning_group_data> outer_groups;
     std::vector<entity_type> entities;
     size_type available{};
     entity_type next{};

+ 2 - 2
test/entt/entity/registry.cpp

@@ -508,10 +508,10 @@ TEST(Registry, PartialOwningGroupInitOnFirstUse) {
     registry.assign<char>(e0, 'c');
 
     const auto e1 = registry.create();
-    registry.assign<int>(e1, 0);
+    registry.assign<int>(e1, 1);
 
     const auto e2 = registry.create();
-    registry.assign<int>(e2, 0);
+    registry.assign<int>(e2, 2);
     registry.assign<char>(e2, 'c');
 
     ASSERT_FALSE(registry.owned<int>());

+ 4 - 4
test/lib/a_module.cpp

@@ -10,10 +10,10 @@
 #endif
 #endif
 
-ENTT_SHARED_TYPE(int);
-ENTT_SHARED_TYPE(char);
-ENTT_SHARED_TYPE(double);
-ENTT_SHARED_TYPE(float);
+ENTT_SHARED_TYPE(int)
+ENTT_SHARED_TYPE(char)
+ENTT_SHARED_TYPE(double)
+ENTT_SHARED_TYPE(float)
 
 LIB_EXPORT typename entt::registry<>::component_type a_module_int_type() {
     entt::registry<> registry;

+ 4 - 4
test/lib/another_module.cpp

@@ -10,10 +10,10 @@
 #endif
 #endif
 
-ENTT_SHARED_TYPE(int);
-ENTT_SHARED_TYPE(char);
-ENTT_SHARED_TYPE(double);
-ENTT_SHARED_TYPE(float);
+ENTT_SHARED_TYPE(int)
+ENTT_SHARED_TYPE(char)
+ENTT_SHARED_TYPE(double)
+ENTT_SHARED_TYPE(float)
 
 LIB_EXPORT typename entt::registry<>::component_type another_module_int_type() {
     entt::registry<> registry;

+ 2 - 2
test/lib/lib.cpp

@@ -6,8 +6,8 @@ extern typename entt::registry<>::component_type a_module_char_type();
 extern typename entt::registry<>::component_type another_module_int_type();
 extern typename entt::registry<>::component_type another_module_char_type();
 
-ENTT_SHARED_TYPE(int);
-ENTT_SHARED_TYPE(char);
+ENTT_SHARED_TYPE(int)
+ENTT_SHARED_TYPE(char)
 
 TEST(Lib, Shared) {
     entt::registry<> registry;