فهرست منبع

registry: uses type_id_v rather than families

Michele Caini 6 سال پیش
والد
کامیت
83bea8b654

+ 5 - 0
TODO

@@ -19,6 +19,7 @@
 * any-of rule for views/groups (eg entity has A and any of B/C/D)
 * any-of rule for views/groups (eg entity has A and any of B/C/D)
 * sparse set: there exists an alternative to paginated sparse arrays?
 * sparse set: there exists an alternative to paginated sparse arrays?
 * see warning (vs2017 only): d:\a\entt\entt\src\entt\entity\registry.hpp(108)
 * see warning (vs2017 only): d:\a\entt\entt\src\entt\entity\registry.hpp(108)
+* remove duktape example (and eventually provide a new one)?
 * registry
 * registry
   - ::assure -> pool_type<T> &
   - ::assure -> pool_type<T> &
   - ::group improve, reduce code
   - ::group improve, reduce code
@@ -26,6 +27,10 @@
 * Mission: get rid of named types
 * Mission: get rid of named types
   - make it possible to use custom generators (eg for plugins)
   - make it possible to use custom generators (eg for plugins)
     * registry
     * registry
+    * remove entt::component
+    * clean-up superfluous to_integer
+    * ENTT_ID_TYPE id() (eg dispatcher) can be a data member rather than a virtual function
+    * define a kind of assure also for context variables
     * meta: use type_id, remove import, everything should work transparently
     * meta: use type_id, remove import, everything should work transparently
     * type_id_enabled and fallback on old-fashioned families otherwise
     * type_id_enabled and fallback on old-fashioned families otherwise
     * add discard pool functionality (+ test)
     * add discard pool functionality (+ test)

+ 70 - 76
src/entt/entity/registry.hpp

@@ -11,9 +11,8 @@
 #include <algorithm>
 #include <algorithm>
 #include <type_traits>
 #include <type_traits>
 #include "../config/config.h"
 #include "../config/config.h"
-#include "../core/family.hpp"
 #include "../core/algorithm.hpp"
 #include "../core/algorithm.hpp"
-#include "../core/attribute.h"
+#include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
 #include "../core/type_traits.hpp"
 #include "../signal/sigh.hpp"
 #include "../signal/sigh.hpp"
 #include "runtime_view.hpp"
 #include "runtime_view.hpp"
@@ -42,15 +41,6 @@ namespace entt {
  */
  */
 template<typename Entity>
 template<typename Entity>
 class basic_registry {
 class basic_registry {
-    struct ENTT_API registry_context_family;
-    struct ENTT_API registry_component_family;
-
-    template<typename Type>
-    using context_family = family<Type, registry_context_family>;
-
-    template<typename Type>
-    using component_family = family<Type, registry_component_family>;
-
     using traits_type = entt_traits<std::underlying_type_t<Entity>>;
     using traits_type = entt_traits<std::underlying_type_t<Entity>>;
 
 
     template<typename Component>
     template<typename Component>
@@ -157,6 +147,7 @@ class basic_registry {
     };
     };
 
 
     struct pool_data {
     struct pool_data {
+        ENTT_ID_TYPE id;
         std::unique_ptr<sparse_set<Entity>> pool;
         std::unique_ptr<sparse_set<Entity>> pool;
         void(* assure)(basic_registry &, const sparse_set<Entity> &);
         void(* assure)(basic_registry &, const sparse_set<Entity> &);
         void(* remove)(sparse_set<Entity> &, basic_registry &, const Entity);
         void(* remove)(sparse_set<Entity> &, basic_registry &, const Entity);
@@ -166,23 +157,28 @@ class basic_registry {
     struct group_data {
     struct group_data {
         std::size_t extent[3];
         std::size_t extent[3];
         std::unique_ptr<void, void(*)(void *)> group;
         std::unique_ptr<void, void(*)(void *)> group;
-        bool(* owned)(const component) ENTT_NOEXCEPT;
-        bool(* get)(const component) ENTT_NOEXCEPT;
-        bool(* exclude)(const component) ENTT_NOEXCEPT;
+        bool(* owned)(const ENTT_ID_TYPE) ENTT_NOEXCEPT;
+        bool(* get)(const ENTT_ID_TYPE) ENTT_NOEXCEPT;
+        bool(* exclude)(const ENTT_ID_TYPE) ENTT_NOEXCEPT;
     };
     };
 
 
     struct basic_variable {
     struct basic_variable {
         virtual ~basic_variable() = default;
         virtual ~basic_variable() = default;
+        virtual ENTT_ID_TYPE id() const ENTT_NOEXCEPT = 0;
     };
     };
 
 
     template<typename Type>
     template<typename Type>
     struct variable_handler: basic_variable {
     struct variable_handler: basic_variable {
+        Type value;
+
         template<typename... Args>
         template<typename... Args>
         variable_handler(Args &&... args)
         variable_handler(Args &&... args)
             : value{std::forward<Args>(args)...}
             : value{std::forward<Args>(args)...}
         {}
         {}
 
 
-        Type value;
+        ENTT_ID_TYPE id() const ENTT_NOEXCEPT override {
+            return type_id_v<Type>;
+        }
     };
     };
 
 
     auto generate() {
     auto generate() {
@@ -214,31 +210,37 @@ class basic_registry {
 
 
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     const pool_type<Component> * assure(Args &&... args) const {
     const pool_type<Component> * assure(Args &&... args) const {
-        const auto ctype = to_integer(type<Component>());
+        static_assert(std::is_same_v<Component, std::decay_t<Component>>);
+        static std::size_t index{pools.size()};
 
 
-        if(!(ctype < pools.size())) {
-            pools.resize(ctype+1);
-        }
+        if(!(index < pools.size()) || pools[index].id != type_id_v<Component>) {
+            index = std::find_if(pools.cbegin(), pools.cend(), [](auto &&cpool) {
+                return cpool.id == type_id_v<Component>;
+            }) - pools.cbegin();
 
 
-        if(!pools[ctype].pool) {
-            pools[ctype].pool = std::make_unique<pool_type<Component>>(std::forward<Args>(args)...);
+            if(index == pools.size()) {
+                auto &&pdata = pools.emplace_back();
 
 
-            pools[ctype].remove = [](sparse_set<Entity> &cpool, basic_registry &owner, const Entity entt) {
-                static_cast<pool_type<Component> &>(cpool).remove(owner, entt);
-            };
+                pdata.id = type_id_v<Component>;
+                pdata.pool = std::make_unique<pool_type<Component>>(std::forward<Args>(args)...);
 
 
-            if constexpr(std::is_copy_constructible_v<std::decay_t<Component>>) {
-                pools[ctype].assure = [](basic_registry &other, const sparse_set<Entity> &cpool) {
-                    other.assure<Component>(static_cast<const pool_type<Component> &>(cpool));
+                pdata.remove = [](sparse_set<Entity> &cpool, basic_registry &owner, const Entity entt) {
+                    static_cast<pool_type<Component> &>(cpool).remove(owner, entt);
                 };
                 };
 
 
-                pools[ctype].stomp = [](basic_registry &other, const Entity dst, const sparse_set<Entity> &cpool, const Entity src) {
-                    other.assign_or_replace<Component>(dst, static_cast<const pool_type<Component> &>(cpool).get(src));
-                };
+                if constexpr(std::is_copy_constructible_v<std::decay_t<Component>>) {
+                    pdata.assure = [](basic_registry &other, const sparse_set<Entity> &cpool) {
+                        other.assure<Component>(static_cast<const pool_type<Component> &>(cpool));
+                    };
+
+                    pdata.stomp = [](basic_registry &other, const Entity dst, const sparse_set<Entity> &cpool, const Entity src) {
+                        other.assign_or_replace<Component>(dst, static_cast<const pool_type<Component> &>(cpool).get(src));
+                    };
+                }
             }
             }
         }
         }
 
 
-        return static_cast<pool_type<Component> *>(pools[ctype].pool.get());
+        return static_cast<pool_type<Component> *>(pools[index].pool.get());
     }
     }
 
 
     template<typename Component>
     template<typename Component>
@@ -263,20 +265,6 @@ public:
     /*! @brief Default move assignment operator. @return This registry. */
     /*! @brief Default move assignment operator. @return This registry. */
     basic_registry & operator=(basic_registry &&) = default;
     basic_registry & operator=(basic_registry &&) = default;
 
 
-    /**
-     * @brief Returns the opaque identifier of a component.
-     *
-     * The given component doesn't need to be necessarily in use.<br/>
-     * Identifiers aren't guaranteed to be stable between different runs.
-     *
-     * @tparam Component Type of component to query.
-     * @return The opaque identifier of the given type of component.
-     */
-    template<typename Component>
-    static component type() ENTT_NOEXCEPT {
-        return component{component_family<std::decay_t<Component>>::type()};
-    }
-
     /**
     /**
      * @brief Prepares a pool for the given type if required.
      * @brief Prepares a pool for the given type if required.
      * @tparam Component Type of component for which to prepare a pool.
      * @tparam Component Type of component for which to prepare a pool.
@@ -285,7 +273,7 @@ public:
      */
      */
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     void prepare(Args &&... args) {
     void prepare(Args &&... args) {
-        ENTT_ASSERT(!(to_integer(type<Component>()) < pools.size()) || !pools[to_integer(type<Component>())].pool);
+        ENTT_ASSERT(std::none_of(pools.cbegin(), pools.cend(), [](auto &&pdata) { return pdata.id == type_id_v<Component>; }));
         assure<Component>(std::forward<Args>(args)...);
         assure<Component>(std::forward<Args>(args)...);
     }
     }
 
 
@@ -569,7 +557,7 @@ public:
         ENTT_ASSERT(valid(entity));
         ENTT_ASSERT(valid(entity));
 
 
         for(auto pos = pools.size(); pos; --pos) {
         for(auto pos = pools.size(); pos; --pos) {
-            if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->has(entity)) {
+            if(auto &pdata = pools[pos-1]; pdata.pool->has(entity)) {
                 pdata.remove(*pdata.pool, *this, entity);
                 pdata.remove(*pdata.pool, *this, entity);
             }
             }
         }
         }
@@ -956,8 +944,7 @@ public:
         bool orphan = true;
         bool orphan = true;
 
 
         for(std::size_t pos{}, last = pools.size(); pos < last && orphan; ++pos) {
         for(std::size_t pos{}, last = pools.size(); pos < last && orphan; ++pos) {
-            const auto &pdata = pools[pos];
-            orphan = !(pdata.pool && pdata.pool->has(entity));
+            orphan = !pools[pos].pool->has(entity);
         }
         }
 
 
         return orphan;
         return orphan;
@@ -1216,7 +1203,7 @@ public:
     template<typename... Component, typename... Exclude>
     template<typename... Component, typename... Exclude>
     entt::basic_view<Entity, exclude_t<Exclude...>, Component...> view(exclude_t<Exclude...> = {}) {
     entt::basic_view<Entity, exclude_t<Exclude...>, Component...> view(exclude_t<Exclude...> = {}) {
         static_assert(sizeof...(Component) > 0);
         static_assert(sizeof...(Component) > 0);
-        return { assure<Component>()..., assure<Exclude>()... };
+        return { assure<std::decay_t<Component>>()..., assure<Exclude>()... };
     }
     }
 
 
     /*! @copydoc view */
     /*! @copydoc view */
@@ -1272,15 +1259,15 @@ public:
         using handler_type = group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...>;
         using handler_type = group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...>;
 
 
         [[maybe_unused]] constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
         [[maybe_unused]] constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
-        const auto cpools = std::make_tuple(assure<Owned>()..., assure<Get>()..., assure<Exclude>()...);
+        const auto cpools = std::make_tuple(assure<std::decay_t<Owned>>()..., assure<std::decay_t<Get>>()..., assure<Exclude>()...);
         const std::size_t extent[3]{sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)};
         const std::size_t extent[3]{sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)};
         handler_type *handler = nullptr;
         handler_type *handler = nullptr;
 
 
         if(auto it = std::find_if(groups.cbegin(), groups.cend(), [&extent](const auto &gdata) {
         if(auto it = std::find_if(groups.cbegin(), groups.cend(), [&extent](const auto &gdata) {
             return std::equal(std::begin(extent), std::end(extent), std::begin(gdata.extent))
             return std::equal(std::begin(extent), std::end(extent), std::begin(gdata.extent))
-                    && (gdata.owned(type<Owned>()) && ...)
-                    && (gdata.get(type<Get>()) && ...)
-                    && (gdata.exclude(type<Exclude>()) && ...);
+                    && (gdata.owned(type_id_v<std::decay_t<Owned>>) && ...)
+                    && (gdata.get(type_id_v<std::decay_t<Get>>) && ...)
+                    && (gdata.exclude(type_id_v<Exclude>) && ...);
         }); it != groups.cend())
         }); it != groups.cend())
         {
         {
             handler = static_cast<handler_type *>(it->group.get());
             handler = static_cast<handler_type *>(it->group.get());
@@ -1293,26 +1280,31 @@ public:
             group_data gdata{
             group_data gdata{
                 { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) },
                 { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) },
                 decltype(group_data::group){new handler_type{cpools}, [](void *gptr) { delete static_cast<handler_type *>(gptr); }},
                 decltype(group_data::group){new handler_type{cpools}, [](void *gptr) { delete static_cast<handler_type *>(gptr); }},
-                [](const component ctype) ENTT_NOEXCEPT { return ((ctype == type<Owned>()) || ...); },
-                [](const component ctype) ENTT_NOEXCEPT { return ((ctype == type<Get>()) || ...); },
-                [](const component ctype) ENTT_NOEXCEPT { return ((ctype == type<Exclude>()) || ...); }
+                [](const auto ctype) ENTT_NOEXCEPT { return ((ctype == type_id_v<std::decay_t<Owned>>) || ...); },
+                [](const auto ctype) ENTT_NOEXCEPT { return ((ctype == type_id_v<std::decay_t<Get>>) || ...); },
+                [](const auto ctype) ENTT_NOEXCEPT { return ((ctype == type_id_v<Exclude>) || ...); }
             };
             };
 
 
             if constexpr(sizeof...(Owned) == 0) {
             if constexpr(sizeof...(Owned) == 0) {
                 handler = static_cast<handler_type *>(groups.emplace_back(std::move(gdata)).group.get());
                 handler = static_cast<handler_type *>(groups.emplace_back(std::move(gdata)).group.get());
             } else {
             } else {
                 ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&extent](const auto &curr) {
                 ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&extent](const auto &curr) {
-                    const std::size_t diff[3]{ (0u + ... + curr.owned(type<Owned>())), (0u + ... + curr.get(type<Get>())), (0u + ... + curr.exclude(type<Exclude>())) };
+                    const std::size_t diff[3]{
+                        (0u + ... + curr.owned(type_id_v<std::decay_t<Owned>>)),
+                        (0u + ... + curr.get(type_id_v<std::decay_t<Get>>)),
+                        (0u + ... + curr.exclude(type_id_v<Exclude>))
+                    };
+
                     return !diff[0] || ((std::equal(std::begin(diff), std::end(diff), extent) || std::equal(std::begin(diff), std::end(diff), curr.extent)));
                     return !diff[0] || ((std::equal(std::begin(diff), std::end(diff), extent) || std::equal(std::begin(diff), std::end(diff), curr.extent)));
                 }));
                 }));
 
 
                 const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [&size](const auto &curr) {
                 const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [&size](const auto &curr) {
-                    const std::size_t diff = (0u + ... + curr.owned(type<Owned>()));
+                    const std::size_t diff = (0u + ... + curr.owned(type_id_v<std::decay_t<Owned>>));
                     return !diff || (size > (curr.extent[0] + curr.extent[1] + curr.extent[2]));
                     return !diff || (size > (curr.extent[0] + curr.extent[1] + curr.extent[2]));
                 });
                 });
 
 
                 const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &curr) {
                 const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &curr) {
-                    return (0u + ... + curr.owned(type<Owned>()));
+                    return (0u + ... + curr.owned(type_id_v<std::decay_t<Owned>>));
                 });
                 });
 
 
                 maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get());
                 maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get());
@@ -1432,7 +1424,11 @@ public:
         std::vector<const sparse_set<Entity> *> selected(std::distance(first, last));
         std::vector<const sparse_set<Entity> *> selected(std::distance(first, last));
 
 
         std::transform(first, last, selected.begin(), [this](const component ctype) {
         std::transform(first, last, selected.begin(), [this](const component ctype) {
-            return to_integer(ctype) < pools.size() ? pools[to_integer(ctype)].pool.get() : nullptr;
+            const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) {
+                return pdata.id == to_integer(ctype);
+            });
+
+            return it == pools.cend() ? nullptr : it->pool.get();
         });
         });
 
 
         return { std::move(selected) };
         return { std::move(selected) };
@@ -1480,7 +1476,7 @@ public:
 
 
         if constexpr(sizeof...(Component) == 0) {
         if constexpr(sizeof...(Component) == 0) {
             for(size_type pos{}; pos < pools.size(); ++pos) {
             for(size_type pos{}; pos < pools.size(); ++pos) {
-                if(const auto &pdata = pools[pos]; pdata.assure && ((pos != to_integer(type<Exclude>())) && ...)) {
+                if(const auto &pdata = pools[pos]; pdata.assure && ((pdata.id != type_id_v<Exclude>) && ...)) {
                     pdata.assure(other, *pdata.pool);
                     pdata.assure(other, *pdata.pool);
                 }
                 }
             }
             }
@@ -1533,7 +1529,7 @@ public:
     void stomp(const entity_type dst, const basic_registry &other, const entity_type src, exclude_t<Exclude...> = {}) {
     void stomp(const entity_type dst, const basic_registry &other, const entity_type src, exclude_t<Exclude...> = {}) {
         if constexpr(sizeof...(Component) == 0) {
         if constexpr(sizeof...(Component) == 0) {
             for(size_type pos{}; pos < other.pools.size(); ++pos) {
             for(size_type pos{}; pos < other.pools.size(); ++pos) {
-                if(const auto &pdata = other.pools[pos]; pdata.stomp && ((pos != to_integer(type<Exclude>())) && ...) && pdata.pool->has(src)) {
+                if(const auto &pdata = other.pools[pos]; pdata.stomp && ((pdata.id != type_id_v<Exclude>) && ...) && pdata.pool->has(src)) {
                     pdata.stomp(*this, dst, *pdata.pool, src);
                     pdata.stomp(*this, dst, *pdata.pool, src);
                 }
                 }
             }
             }
@@ -1629,14 +1625,9 @@ public:
      */
      */
     template<typename Type, typename... Args>
     template<typename Type, typename... Args>
     Type & set(Args &&... args) {
     Type & set(Args &&... args) {
-        const auto vtype = context_family<std::decay_t<Type>>::type();
-
-        if(!(vtype < vars.size())) {
-            vars.resize(vtype+1);
-        }
-
-        vars[vtype] = std::make_unique<variable_handler<Type>>(std::forward<Args>(args)...);
-        return static_cast<variable_handler<Type> &>(*vars[vtype]).value;
+        unset<Type>();
+        vars.push_back(std::make_unique<variable_handler<Type>>(std::forward<Args>(args)...));
+        return static_cast<variable_handler<Type> &>(*vars.back()).value;
     }
     }
 
 
     /**
     /**
@@ -1645,9 +1636,9 @@ public:
      */
      */
     template<typename Type>
     template<typename Type>
     void unset() {
     void unset() {
-        if(const auto vtype = context_family<std::decay_t<Type>>::type(); vtype < vars.size()) {
-            vars[vtype].reset();
-        }
+        vars.erase(std::remove_if(vars.begin(), vars.end(), [](auto &&handler) {
+            return handler->id() == type_id_v<Type>;
+        }), vars.end());
     }
     }
 
 
     /**
     /**
@@ -1675,8 +1666,11 @@ public:
      */
      */
     template<typename Type>
     template<typename Type>
     const Type * try_ctx() const {
     const Type * try_ctx() const {
-        const auto vtype = context_family<std::decay_t<Type>>::type();
-        return vtype < vars.size() && vars[vtype] ? &static_cast<variable_handler<Type> &>(*vars[vtype]).value : nullptr;
+        auto it = std::find_if(vars.cbegin(), vars.cend(), [](auto &&handler) {
+            return handler->id() == type_id_v<Type>;
+        });
+
+        return it == vars.cend() ? nullptr : &static_cast<const variable_handler<Type> &>(*it->get()).value;
     }
     }
 
 
     /*! @copydoc try_ctx */
     /*! @copydoc try_ctx */

+ 1 - 1
src/entt/signal/dispatcher.hpp

@@ -87,7 +87,7 @@ class dispatcher {
         if(!(index < pools.size()) || pools[index]->id() != type_id_v<Event>) {
         if(!(index < pools.size()) || pools[index]->id() != type_id_v<Event>) {
             index = std::find_if(pools.cbegin(), pools.cend(), [](auto &&cpool) {
             index = std::find_if(pools.cbegin(), pools.cend(), [](auto &&cpool) {
                 return cpool->id() == type_id_v<Event>;
                 return cpool->id() == type_id_v<Event>;
-            }) - pools.begin();
+            }) - pools.cbegin();
 
 
             if(index == pools.size()) {
             if(index == pools.size()) {
                 pools.push_back(std::make_unique<pool_handler<Event>>());
                 pools.push_back(std::make_unique<pool_handler<Event>>());

+ 1 - 1
src/entt/signal/emitter.hpp

@@ -126,7 +126,7 @@ class emitter {
         if(!(index < pools.size()) || pools[index]->id() != type_id_v<Event>) {
         if(!(index < pools.size()) || pools[index]->id() != type_id_v<Event>) {
             index = std::find_if(pools.cbegin(), pools.cend(), [](auto &&cpool) {
             index = std::find_if(pools.cbegin(), pools.cend(), [](auto &&cpool) {
                 return cpool->id() == type_id_v<Event>;
                 return cpool->id() == type_id_v<Event>;
-            }) - pools.begin();
+            }) - pools.cbegin();
 
 
             if(index == pools.size()) {
             if(index == pools.size()) {
                 pools.push_back(std::make_unique<pool_handler<Event>>());
                 pools.push_back(std::make_unique<pool_handler<Event>>());

+ 23 - 22
test/benchmark/benchmark.cpp

@@ -4,6 +4,7 @@
 #include <chrono>
 #include <chrono>
 #include <iterator>
 #include <iterator>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <entt/core/type_info.hpp>
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
 
 
 struct position {
 struct position {
@@ -167,7 +168,7 @@ TEST(Benchmark, IterateSingleComponentRuntime1M) {
     }
     }
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
-        entt::component types[] = { registry.type<position>() };
+        entt::component types[] = { entt::component{entt::type_id_v<position>} };
 
 
         timer timer;
         timer timer;
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -332,7 +333,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1M) {
     }
     }
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
-        entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
+        entt::component types[] = { entt::component{entt::type_id_v<position>}, entt::component{entt::type_id_v<velocity>} };
 
 
         timer timer;
         timer timer;
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -360,7 +361,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) {
     }
     }
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
-        entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
+        entt::component types[] = { entt::component{entt::type_id_v<position>}, entt::component{entt::type_id_v<velocity>} };
 
 
         timer timer;
         timer timer;
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -388,7 +389,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MOne) {
     }
     }
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
-        entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
+        entt::component types[] = { entt::component{entt::type_id_v<position>}, entt::component{entt::type_id_v<velocity>} };
 
 
         timer timer;
         timer timer;
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -561,7 +562,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1M) {
     }
     }
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
-        entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
+        entt::component types[] = { entt::component{entt::type_id_v<position>}, entt::component{entt::type_id_v<velocity>}, entt::component{entt::type_id_v<comp<0>>} };
 
 
         timer timer;
         timer timer;
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -591,7 +592,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MHalf) {
     }
     }
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
-        entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
+        entt::component types[] = { entt::component{entt::type_id_v<position>}, entt::component{entt::type_id_v<velocity>}, entt::component{entt::type_id_v<comp<0>>} };
 
 
         timer timer;
         timer timer;
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -621,7 +622,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MOne) {
     }
     }
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
-        entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
+        entt::component types[] = { entt::component{entt::type_id_v<position>}, entt::component{entt::type_id_v<velocity>}, entt::component{entt::type_id_v<comp<0>>} };
 
 
         timer timer;
         timer timer;
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
         registry.runtime_view(std::begin(types), std::end(types)).each(func);
@@ -836,11 +837,11 @@ TEST(Benchmark, IterateFiveComponentsRuntime1M) {
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
         entt::component types[] = {
         entt::component types[] = {
-            registry.type<position>(),
-            registry.type<velocity>(),
-            registry.type<comp<0>>(),
-            registry.type<comp<1>>(),
-            registry.type<comp<2>>()
+            entt::component{entt::type_id_v<position>},
+            entt::component{entt::type_id_v<velocity>},
+            entt::component{entt::type_id_v<comp<0>>},
+            entt::component{entt::type_id_v<comp<1>>},
+            entt::component{entt::type_id_v<comp<2>>}
         };
         };
 
 
         timer timer;
         timer timer;
@@ -876,11 +877,11 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) {
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
         entt::component types[] = {
         entt::component types[] = {
-            registry.type<position>(),
-            registry.type<velocity>(),
-            registry.type<comp<0>>(),
-            registry.type<comp<1>>(),
-            registry.type<comp<2>>()
+            entt::component{entt::type_id_v<position>},
+            entt::component{entt::type_id_v<velocity>},
+            entt::component{entt::type_id_v<comp<0>>},
+            entt::component{entt::type_id_v<comp<1>>},
+            entt::component{entt::type_id_v<comp<2>>}
         };
         };
 
 
         timer timer;
         timer timer;
@@ -916,11 +917,11 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
 
 
     auto test = [&registry](auto func) {
     auto test = [&registry](auto func) {
         entt::component types[] = {
         entt::component types[] = {
-            registry.type<position>(),
-            registry.type<velocity>(),
-            registry.type<comp<0>>(),
-            registry.type<comp<1>>(),
-            registry.type<comp<2>>()
+            entt::component{entt::type_id_v<position>},
+            entt::component{entt::type_id_v<velocity>},
+            entt::component{entt::type_id_v<comp<0>>},
+            entt::component{entt::type_id_v<comp<1>>},
+            entt::component{entt::type_id_v<comp<2>>}
         };
         };
 
 
         timer timer;
         timer timer;

+ 4 - 6
test/entt/entity/helper.cpp

@@ -10,9 +10,8 @@ TEST(Helper, AsView) {
 
 
     ([](entt::view<entt::exclude_t<>, int>) {})(entt::as_view{registry});
     ([](entt::view<entt::exclude_t<>, int>) {})(entt::as_view{registry});
     ([](entt::view<entt::exclude_t<int>, char, double>) {})(entt::as_view{registry});
     ([](entt::view<entt::exclude_t<int>, char, double>) {})(entt::as_view{registry});
-    ([](entt::view<entt::exclude_t<const int>, char, double>) {})(entt::as_view{registry});
-    ([](entt::view<entt::exclude_t<const int>, const char, double>) {})(entt::as_view{registry});
-    ([](entt::view<entt::exclude_t<const int>, const char, const double>) {})(entt::as_view{registry});
+    ([](entt::view<entt::exclude_t<int>, const char, double>) {})(entt::as_view{registry});
+    ([](entt::view<entt::exclude_t<int>, const char, const double>) {})(entt::as_view{registry});
 }
 }
 
 
 TEST(Helper, AsGroup) {
 TEST(Helper, AsGroup) {
@@ -20,9 +19,8 @@ TEST(Helper, AsGroup) {
     const entt::registry cregistry;
     const entt::registry cregistry;
 
 
     ([](entt::group<entt::exclude_t<int>, entt::get_t<char>, double>) {})(entt::as_group{registry});
     ([](entt::group<entt::exclude_t<int>, entt::get_t<char>, double>) {})(entt::as_group{registry});
-    ([](entt::group<entt::exclude_t<const int>, entt::get_t<char>, double>) {})(entt::as_group{registry});
-    ([](entt::group<entt::exclude_t<const int>, entt::get_t<const char>, double>) {})(entt::as_group{registry});
-    ([](entt::group<entt::exclude_t<const int>, entt::get_t<const char>, const double>) {})(entt::as_group{registry});
+    ([](entt::group<entt::exclude_t<int>, entt::get_t<const char>, double>) {})(entt::as_group{registry});
+    ([](entt::group<entt::exclude_t<int>, entt::get_t<const char>, const double>) {})(entt::as_group{registry});
 }
 }
 
 
 TEST(Helper, Tag) {
 TEST(Helper, Tag) {

+ 0 - 8
test/entt/entity/registry.cpp

@@ -78,12 +78,6 @@ TEST(Registry, Context) {
     ASSERT_EQ(registry.try_ctx<float>(), nullptr);
     ASSERT_EQ(registry.try_ctx<float>(), nullptr);
 }
 }
 
 
-TEST(Registry, Types) {
-    entt::registry registry;
-    ASSERT_EQ(registry.type<int>(), registry.type<int>());
-    ASSERT_NE(registry.type<double>(), registry.type<int>());
-}
-
 TEST(Registry, Functionalities) {
 TEST(Registry, Functionalities) {
     entt::registry registry;
     entt::registry registry;
 
 
@@ -1540,9 +1534,7 @@ TEST(Registry, Constness) {
     entt::registry registry;
     entt::registry registry;
 
 
     ASSERT_TRUE((std::is_same_v<decltype(registry.assign<int>({})), int &>));
     ASSERT_TRUE((std::is_same_v<decltype(registry.assign<int>({})), int &>));
-    ASSERT_TRUE((std::is_same_v<decltype(registry.assign<const int>({})), int &>));
     ASSERT_TRUE((std::is_same_v<decltype(registry.assign<empty_type>({})), empty_type>));
     ASSERT_TRUE((std::is_same_v<decltype(registry.assign<empty_type>({})), empty_type>));
-    ASSERT_TRUE((std::is_same_v<decltype(registry.assign<const empty_type>({})), empty_type>));
 
 
     ASSERT_TRUE((std::is_same_v<decltype(registry.get<int>({})), int &>));
     ASSERT_TRUE((std::is_same_v<decltype(registry.get<int>({})), int &>));
     ASSERT_TRUE((std::is_same_v<decltype(registry.get<int, char>({})), std::tuple<int &, char &>>));
     ASSERT_TRUE((std::is_same_v<decltype(registry.get<int, char>({})), std::tuple<int &, char &>>));

+ 7 - 7
test/entt/entity/runtime_view.cpp

@@ -11,7 +11,7 @@ TEST(RuntimeView, Functionalities) {
     registry.reserve<int>(0);
     registry.reserve<int>(0);
     registry.reserve<char>(0);
     registry.reserve<char>(0);
 
 
-    entt::component types[] = { registry.type<int>(), registry.type<char>() };
+    entt::component types[] = { entt::component{entt::type_id_v<int>}, entt::component{entt::type_id_v<char>} };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
 
     ASSERT_TRUE(view.empty());
     ASSERT_TRUE(view.empty());
@@ -54,7 +54,7 @@ TEST(RuntimeView, Iterator) {
     registry.assign<int>(entity);
     registry.assign<int>(entity);
     registry.assign<char>(entity);
     registry.assign<char>(entity);
 
 
-    entt::component types[] = { registry.type<int>(), registry.type<char>() };
+    entt::component types[] = { entt::component{entt::type_id_v<int>}, entt::component{entt::type_id_v<char>} };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     using iterator_type = typename decltype(view)::iterator_type;
     using iterator_type = typename decltype(view)::iterator_type;
 
 
@@ -90,7 +90,7 @@ TEST(RuntimeView, Contains) {
 
 
     registry.destroy(e0);
     registry.destroy(e0);
 
 
-    entt::component types[] = { registry.type<int>(), registry.type<char>() };
+    entt::component types[] = { entt::component{entt::type_id_v<int>}, entt::component{entt::type_id_v<char>} };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
 
     ASSERT_FALSE(view.contains(e0));
     ASSERT_FALSE(view.contains(e0));
@@ -109,7 +109,7 @@ TEST(RuntimeView, Empty) {
     registry.assign<char>(e1);
     registry.assign<char>(e1);
     registry.assign<float>(e1);
     registry.assign<float>(e1);
 
 
-    entt::component types[] = { registry.type<char>(), registry.type<int>(), registry.type<float>() };
+    entt::component types[] = { entt::component{entt::type_id_v<int>}, entt::component{entt::type_id_v<char>}, entt::component{entt::type_id_v<float>} };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
 
     view.each([](auto) { FAIL(); });
     view.each([](auto) { FAIL(); });
@@ -129,7 +129,7 @@ TEST(RuntimeView, Each) {
     registry.assign<int>(e1);
     registry.assign<int>(e1);
     registry.assign<char>(e1);
     registry.assign<char>(e1);
 
 
-    entt::component types[] = { registry.type<int>(), registry.type<char>() };
+    entt::component types[] = { entt::component{entt::type_id_v<int>}, entt::component{entt::type_id_v<char>} };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     std::size_t cnt = 0;
     std::size_t cnt = 0;
 
 
@@ -151,7 +151,7 @@ TEST(RuntimeView, EachWithHoles) {
     registry.assign<int>(e0, 0);
     registry.assign<int>(e0, 0);
     registry.assign<int>(e2, 2);
     registry.assign<int>(e2, 2);
 
 
-    entt::component types[] = { registry.type<int>(), registry.type<char>() };
+    entt::component types[] = { entt::component{entt::type_id_v<int>}, entt::component{entt::type_id_v<char>} };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
 
     view.each([e0](auto entity) {
     view.each([e0](auto entity) {
@@ -165,7 +165,7 @@ TEST(RuntimeView, MissingPool) {
     const auto e0 = registry.create();
     const auto e0 = registry.create();
     registry.assign<int>(e0);
     registry.assign<int>(e0);
 
 
-    entt::component types[] = { registry.type<int>(), registry.type<char>() };
+    entt::component types[] = { entt::component{entt::type_id_v<int>}, entt::component{entt::type_id_v<char>} };
     auto view = registry.runtime_view(std::begin(types), std::end(types));
     auto view = registry.runtime_view(std::begin(types), std::end(types));
 
 
     ASSERT_TRUE(view.empty());
     ASSERT_TRUE(view.empty());

+ 0 - 18
test/lib/registry/lib.cpp

@@ -2,24 +2,6 @@
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
 #include "types.h"
 #include "types.h"
 
 
-ENTT_API entt::component int_type() {
-    entt::registry registry;
-
-    (void)registry.type<double>();
-    (void)registry.type<float>();
-
-    return registry.type<int>();
-}
-
-ENTT_API entt::component char_type() {
-    entt::registry registry;
-
-    (void)registry.type<double>();
-    (void)registry.type<float>();
-
-    return registry.type<char>();
-}
-
 ENTT_API void update_position(int delta, entt::registry &registry) {
 ENTT_API void update_position(int delta, entt::registry &registry) {
     registry.view<position, velocity>().each([delta](auto &pos, auto &vel) {
     registry.view<position, velocity>().each([delta](auto &pos, auto &vel) {
         pos.x += delta * vel.dx;
         pos.x += delta * vel.dx;

+ 0 - 16
test/lib/registry/main.cpp

@@ -4,25 +4,9 @@
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
 #include "types.h"
 #include "types.h"
 
 
-ENTT_API entt::component int_type();
-ENTT_API entt::component char_type();
-
 ENTT_API void update_position(int, entt::registry &);
 ENTT_API void update_position(int, entt::registry &);
 ENTT_API void assign_velocity(int, entt::registry &);
 ENTT_API void assign_velocity(int, entt::registry &);
 
 
-TEST(Lib, Types) {
-    entt::registry registry;
-
-    ASSERT_EQ(registry.type<int>(), registry.type<const int>());
-    ASSERT_EQ(registry.type<char>(), registry.type<const char>());
-
-    ASSERT_EQ(registry.type<int>(), int_type());
-    ASSERT_EQ(registry.type<char>(), char_type());
-
-    ASSERT_EQ(registry.type<const char>(), char_type());
-    ASSERT_EQ(registry.type<const int>(), int_type());
-}
-
 TEST(Lib, Registry) {
 TEST(Lib, Registry) {
     entt::registry registry;
     entt::registry registry;
 
 

+ 18 - 22
test/mod/mod.cpp

@@ -4,6 +4,7 @@
 #include <string>
 #include <string>
 #include <duktape.h>
 #include <duktape.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <entt/core/type_info.hpp>
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
 
 
 template<typename Type>
 template<typename Type>
@@ -122,9 +123,6 @@ duk_ret_t get(duk_context *ctx, entt::registry &registry) {
 }
 }
 
 
 class duktape_registry {
 class duktape_registry {
-    // I'm pretty sure I won't have more than 99 components in the example
-    static constexpr ENTT_ID_TYPE udef = 100;
-
     struct func_map {
     struct func_map {
         using func_type = duk_ret_t(*)(duk_context *, entt::registry &);
         using func_type = duk_ret_t(*)(duk_context *, entt::registry &);
 
 
@@ -136,7 +134,7 @@ class duktape_registry {
 
 
     template<typename... Comp>
     template<typename... Comp>
     void reg() {
     void reg() {
-        ((func[to_integer(registry.type<Comp>())] = {
+        ((func[entt::type_id_v<Comp>] = {
                 &::set<Comp>,
                 &::set<Comp>,
                 &::unset<Comp>,
                 &::unset<Comp>,
                 &::has<Comp>,
                 &::has<Comp>,
@@ -162,13 +160,11 @@ class duktape_registry {
         auto &registry = dreg.registry;
         auto &registry = dreg.registry;
         auto type = duk_require_uint(ctx, 1);
         auto type = duk_require_uint(ctx, 1);
 
 
-        if(type >= udef) {
-            type = to_integer(registry.type<duktape_runtime>());
-        }
-
-        assert(func.find(type) != func.cend());
+        const auto it = func.find(type);
 
 
-        return (func[type].*Op)(ctx, registry);
+        return (it == func.cend())
+                ? (func[entt::type_id_v<duktape_runtime>].*Op)(ctx, registry)
+                : (it->second.*Op)(ctx, registry);
     }
     }
 
 
 public:
 public:
@@ -179,7 +175,7 @@ public:
     }
     }
 
 
     static duk_ret_t identifier(duk_context *ctx) {
     static duk_ret_t identifier(duk_context *ctx) {
-        static auto next = udef;
+        static ENTT_ID_TYPE next{};
         duk_push_uint(ctx, next++);
         duk_push_uint(ctx, next++);
         return 1;
         return 1;
     }
     }
@@ -210,7 +206,6 @@ public:
     static duk_ret_t entities(duk_context *ctx) {
     static duk_ret_t entities(duk_context *ctx) {
         const duk_idx_t nargs = duk_get_top(ctx);
         const duk_idx_t nargs = duk_get_top(ctx);
         auto &dreg = instance(ctx);
         auto &dreg = instance(ctx);
-        duk_uarridx_t pos = 0;
 
 
         duk_push_array(ctx);
         duk_push_array(ctx);
 
 
@@ -220,18 +215,19 @@ public:
         for(duk_idx_t arg = 0; arg < nargs; arg++) {
         for(duk_idx_t arg = 0; arg < nargs; arg++) {
             auto type = duk_require_uint(ctx, arg);
             auto type = duk_require_uint(ctx, arg);
 
 
-            if(type < udef) {
-                components.push_back(entt::component{type});
-            } else {
+            if(dreg.func.find(type) == dreg.func.cend()) {
                 if(runtime.empty()) {
                 if(runtime.empty()) {
-                    components.push_back(dreg.registry.type<duktape_runtime>());
+                    components.push_back(entt::component{entt::type_id_v<duktape_runtime>});
                 }
                 }
 
 
                 runtime.push_back(entt::component{type});
                 runtime.push_back(entt::component{type});
+            } else {
+                components.push_back(entt::component{type});
             }
             }
         }
         }
 
 
         auto view = dreg.registry.runtime_view(components.cbegin(), components.cend());
         auto view = dreg.registry.runtime_view(components.cbegin(), components.cend());
+        duk_uarridx_t pos = 0;
 
 
         for(const auto entity: view) {
         for(const auto entity: view) {
             if(runtime.empty()) {
             if(runtime.empty()) {
@@ -269,15 +265,15 @@ const duk_function_list_entry js_duktape_registry_methods[] = {
     { nullptr, nullptr, 0 }
     { nullptr, nullptr, 0 }
 };
 };
 
 
-void export_types(duk_context *context, entt::registry &registry) {
-    auto export_type = [idx = duk_push_object(context)](auto *ctx, auto &reg, auto type, const auto *name) {
+void export_types(duk_context *context) {
+    auto export_type = [idx = duk_push_object(context)](auto *ctx, auto type, const auto *name) {
         duk_push_string(ctx, name);
         duk_push_string(ctx, name);
-        duk_push_uint(ctx, to_integer(reg.template type<typename decltype(type)::type>()));
+        duk_push_uint(ctx, entt::type_id_v<typename decltype(type)::type>);
         duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
         duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
     };
     };
 
 
-    export_type(context, registry, tag<position>{}, "position");
-    export_type(context, registry, tag<renderable>{}, "renderable");
+    export_type(context, tag<position>{}, "position");
+    export_type(context, tag<renderable>{}, "renderable");
 
 
     duk_put_global_string(context, "Types");
     duk_put_global_string(context, "Types");
 }
 }
@@ -302,7 +298,7 @@ TEST(Mod, Duktape) {
         FAIL();
         FAIL();
     }
     }
 
 
-    export_types(ctx, registry);
+    export_types(ctx);
     export_duktape_registry(ctx, dreg);
     export_duktape_registry(ctx, dreg);
 
 
     const char *s0 = ""
     const char *s0 = ""

+ 33 - 0
test/plugin/registry/main.cpp

@@ -0,0 +1,33 @@
+#define CR_HOST
+
+#include <cr.h>
+#include <gtest/gtest.h>
+#include <entt/entity/registry.hpp>
+#include "types.h"
+
+TEST(Lib, Registry) {
+    entt::registry registry;
+
+    for(auto i = 0; i < 3; ++i) {
+        const auto entity = registry.create();
+        registry.assign<position>(entity, i, i+1);
+    }
+
+    ASSERT_FALSE(registry.empty<position>());
+    ASSERT_TRUE(registry.empty<velocity>());
+
+    cr_plugin ctx;
+    ctx.userdata = &registry;
+    cr_plugin_load(ctx, PLUGIN);
+    cr_plugin_update(ctx);
+
+    ASSERT_FALSE(registry.empty<position>());
+    ASSERT_FALSE(registry.empty<velocity>());
+
+    registry.view<position>().each([](auto entity, auto &position) {
+        ASSERT_EQ(position.x, entt::to_integer(entity) + 2);
+        ASSERT_EQ(position.y, entt::to_integer(entity) + 3);
+    });
+
+    cr_plugin_close(ctx);
+}

+ 30 - 0
test/plugin/registry/plugin.cpp

@@ -0,0 +1,30 @@
+#include <cr.h>
+#include <entt/entity/registry.hpp>
+#include "types.h"
+
+CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
+    switch (operation) {
+    case CR_STEP:
+        [ctx]() {
+            auto *registry = static_cast<entt::registry *>(ctx->userdata);
+            registry->reset<velocity>();
+
+            for(auto entity: registry->view<position>()) {
+                registry->assign<velocity>(entity, 1, 1);
+            }
+
+            registry->view<position, velocity>().each([](auto &pos, auto &vel) {
+                pos.x += 2 * vel.dx;
+                pos.y += 2 * vel.dy;
+            });
+        }();
+        break;
+    case CR_LOAD:
+    case CR_UNLOAD:
+    case CR_CLOSE:
+        // nothing to do here, this is only a test.
+        break;
+    }
+
+    return 0;
+}

+ 14 - 0
test/plugin/registry/types.h

@@ -0,0 +1,14 @@
+#ifndef ENTT_PLUGIN_REGISTRY_TYPES_H
+#define ENTT_PLUGIN_REGISTRY_TYPES_H
+
+struct position {
+    int x;
+    int y;
+};
+
+struct velocity {
+    int dx;
+    int dy;
+};
+
+#endif // ENTT_PLUGIN_REGISTRY_TYPES_H