Jelajahi Sumber

lib: standalone mode is now the default

Michele Caini 5 tahun lalu
induk
melakukan
118c14bf1e

+ 13 - 22
docs/md/lib.md

@@ -19,9 +19,8 @@
 general and on GNU/Linux when default visibility was set to hidden. The
 general and on GNU/Linux when default visibility was set to hidden. The
 limitation was mainly due to a custom utility used to assign unique, sequential
 limitation was mainly due to a custom utility used to assign unique, sequential
 identifiers with different types.<br/>
 identifiers with different types.<br/>
-Fortunately, nowadays using `EnTT` across boundaries is straightforward. In
-fact, everything just works transparently in almost all cases. There are only a
-few exceptions, easy to deal with anyway.
+Fortunately, nowadays using `EnTT` across boundaries is easier. However, use in
+standalone applications is favored and user intervention is otherwise required.
 
 
 ## The EnTT way
 ## The EnTT way
 
 
@@ -50,25 +49,17 @@ In general, these classes don't arouse much interest. The only exceptions are:
 * When working with plugins or shared libraries that don't export any symbol. In
 * When working with plugins or shared libraries that don't export any symbol. In
   this case, `type_index` confuses the other classes by giving potentially wrong
   this case, `type_index` confuses the other classes by giving potentially wrong
   information to them.<br/>
   information to them.<br/>
-  To avoid problems, it's required to provide a custom generator or to suppress
-  the index generation as a whole:
-
-  ```cpp
-  template<typename Type>
-  struct entt::type_index<Type> {};
-  ```
-
-  All classes that use `type_index` perform also a check on the possibility of
-  creating indexes for types. If it's not a viable solution, they fallback on
-  the type id provided by `type_info`. The latter makes everything stable across
-  boundaries.<br/>
-  This is why suppressing the generation of the indexes solves the problem. In
-  case it's still necessary to associate sequential indexes with types, users
-  can refer to the `family` class, although knowing that these will not be
-  stable across boundaries.
+  To avoid problems, it's required to provide a custom generator. Briefly, it's
+  necessary to specialize the `type_index` class and make it point to a context
+  that is also shared between the main application and the dynamically loaded
+  libraries or plugins.<br/>
+  This will make the type system shared and available to the whole application,
+  not just to a particular tool such as the registry or the dispatcher. It means
+  that a call to `type_index::id()` will return the same identifier for the same
+  type from both sides of a boundary and can be used reliably for any purpose.
 
 
 For anyone who needs more details, the test suite contains multiple examples
 For anyone who needs more details, the test suite contains multiple examples
-covering the most common cases.<br/>
+covering the most common cases (see the `lib` directory for all details).<br/>
 It goes without saying that it's impossible to cover all the possible cases.
 It goes without saying that it's impossible to cover all the possible cases.
 However, what is offered should hopefully serve as a basis for all of them.
 However, what is offered should hopefully serve as a basis for all of them.
 
 
@@ -76,8 +67,8 @@ However, what is offered should hopefully serve as a basis for all of them.
 
 
 The runtime reflection system deserves a special mention when it comes to using
 The runtime reflection system deserves a special mention when it comes to using
 it across boundaries.<br/>
 it across boundaries.<br/>
-Since it's linked to a static context to which the visible components are
-attached and different contexts don't relate to each other, they must be
+Since it's linked already to a static context to which the visible components
+are attached and different contexts don't relate to each other, they must be
 _shared_ to allow the use of meta types across boundaries.
 _shared_ to allow the use of meta types across boundaries.
 
 
 Sharing a context is trivial though. First of all, the local one must be
 Sharing a context is trivial though. First of all, the local one must be

+ 0 - 7
src/entt/config/config.h

@@ -70,11 +70,4 @@
 #endif
 #endif
 
 
 
 
-#ifndef ENTT_STANDALONE
-#   define ENTT_FAST_PATH(...) false
-#else
-#   define ENTT_FAST_PATH(Cond) Cond
-#endif
-
-
 #endif
 #endif

+ 14 - 32
src/entt/entity/registry.hpp

@@ -105,39 +105,21 @@ class basic_registry {
 
 
     template<typename Component>
     template<typename Component>
     [[nodiscard]] const pool_t<Entity, Component> & assure() const {
     [[nodiscard]] const pool_t<Entity, Component> & assure() const {
-        const sparse_set<entity_type> *curr;
-
-        if constexpr(ENTT_FAST_PATH(has_type_index_v<Component>)) {
-            const auto index = type_index<Component>::value();
-
-            if(!(index < pools.size())) {
-                pools.resize(size_type(index+1u));
-            }
-
-            if(auto &&pdata = pools[index]; !pdata.pool) {
-                pdata.type_id = type_info<Component>::id();
-                pdata.pool.reset(new pool_t<Entity, Component>{});
-                pdata.erase = +[](sparse_set<Entity> &cpool, basic_registry &owner, const Entity entt) {
-                    static_cast<pool_t<Entity, Component> &>(cpool).erase(owner, entt);
-                };
-            }
-
-            curr = pools[index].pool.get();
-        } else {
-            if(const auto it = std::find_if(pools.cbegin(), pools.cend(), [id = type_info<Component>::id()](const auto &pdata) { return id == pdata.type_id; }); it == pools.cend()) {
-                curr = pools.emplace_back(pool_data{
-                    type_info<Component>::id(),
-                    std::unique_ptr<sparse_set<entity_type>>{new pool_t<Entity, Component>{}},
-                    +[](sparse_set<Entity> &cpool, basic_registry &owner, const Entity entt) {
-                        static_cast<pool_t<Entity, Component> &>(cpool).erase(owner, entt);
-                    }
-                }).pool.get();
-            } else {
-                curr = it->pool.get();
-            }
+        const auto index = type_index<Component>::value();
+        
+        if(!(index < pools.size())) {
+            pools.resize(size_type(index)+1u);
         }
         }
-
-        return *static_cast<const pool_t<Entity, Component> *>(curr);
+        
+        if(auto &&pdata = pools[index]; !pdata.pool) {
+            pdata.type_id = type_info<Component>::id();
+            pdata.pool.reset(new pool_t<Entity, Component>());
+            pdata.erase = +[](sparse_set<Entity> &cpool, basic_registry &owner, const Entity entt) {
+                static_cast<pool_t<Entity, Component> &>(cpool).erase(owner, entt);
+            };
+        }
+        
+        return static_cast<const pool_t<Entity, Component> &>(*pools[index].pool);
     }
     }
 
 
     template<typename Component>
     template<typename Component>

+ 12 - 17
src/entt/signal/dispatcher.hpp

@@ -39,6 +39,8 @@ class dispatcher {
 
 
     template<typename Event>
     template<typename Event>
     struct pool_handler final: basic_pool {
     struct pool_handler final: basic_pool {
+        static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
+
         using signal_type = sigh<void(Event &)>;
         using signal_type = sigh<void(Event &)>;
         using sink_type = typename signal_type::sink_type;
         using sink_type = typename signal_type::sink_type;
 
 
@@ -90,24 +92,17 @@ class dispatcher {
 
 
     template<typename Event>
     template<typename Event>
     [[nodiscard]] pool_handler<Event> & assure() {
     [[nodiscard]] pool_handler<Event> & assure() {
-        static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
-
-        if constexpr(ENTT_FAST_PATH(has_type_index_v<Event>)) {
-            const auto index = type_index<Event>::value();
-
-            if(!(index < pools.size())) {
-                pools.resize(index+1u);
-            }
-
-            if(!pools[index]) {
-                pools[index].reset(new pool_handler<Event>{});
-            }
-
-            return static_cast<pool_handler<Event> &>(*pools[index]);
-        } else {
-            auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
-            return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
+        const auto index = type_index<Event>::value();
+        
+        if(!(index < pools.size())) {
+            pools.resize(std::size_t(index)+1u);
+        }
+        
+        if(!pools[index]) {
+            pools[index].reset(new pool_handler<Event>{});
         }
         }
+        
+        return static_cast<pool_handler<Event> &>(*pools[index]);
     }
     }
 
 
 public:
 public:

+ 12 - 17
src/entt/signal/emitter.hpp

@@ -50,6 +50,8 @@ class emitter {
 
 
     template<typename Event>
     template<typename Event>
     struct pool_handler final: basic_pool {
     struct pool_handler final: basic_pool {
+        static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
+
         using listener_type = std::function<void(Event &, Derived &)>;
         using listener_type = std::function<void(Event &, Derived &)>;
         using element_type = std::pair<bool, listener_type>;
         using element_type = std::pair<bool, listener_type>;
         using container_type = std::list<element_type>;
         using container_type = std::list<element_type>;
@@ -126,24 +128,17 @@ class emitter {
 
 
     template<typename Event>
     template<typename Event>
     [[nodiscard]] const pool_handler<Event> & assure() const {
     [[nodiscard]] const pool_handler<Event> & assure() const {
-        static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
-
-        if constexpr(ENTT_FAST_PATH(has_type_index_v<Event>)) {
-            const auto index = type_index<Event>::value();
-
-            if(!(index < pools.size())) {
-                pools.resize(index+1u);
-            }
-
-            if(!pools[index]) {
-                pools[index].reset(new pool_handler<Event>{});
-            }
-
-            return static_cast<pool_handler<Event> &>(*pools[index]);
-        } else {
-            auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
-            return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
+        const auto index = type_index<Event>::value();
+        
+        if(!(index < pools.size())) {
+            pools.resize(std::size_t(index)+1u);
+        }
+        
+        if(!pools[index]) {
+            pools[index].reset(new pool_handler<Event>{});
         }
         }
+        
+        return static_cast<pool_handler<Event> &>(*pools[index]);
     }
     }
 
 
     template<typename Event>
     template<typename Event>

+ 12 - 2
test/lib/dispatcher_plugin/main.cpp

@@ -4,10 +4,16 @@
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <entt/core/type_info.hpp>
 #include <entt/core/type_info.hpp>
 #include <entt/signal/dispatcher.hpp>
 #include <entt/signal/dispatcher.hpp>
+#include "type_context.h"
 #include "types.h"
 #include "types.h"
 
 
 template<typename Type>
 template<typename Type>
-struct entt::type_index<Type> {};
+struct entt::type_index<Type> {
+    [[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+        static const entt::id_type value = type_context::instance()->value(entt::type_info<Type>::id());
+        return value;
+    }
+};
 
 
 struct listener {
 struct listener {
     void on(message msg) { value = msg.payload; }
     void on(message msg) { value = msg.payload; }
@@ -23,8 +29,12 @@ TEST(Lib, Dispatcher) {
     dispatcher.sink<message>().connect<&listener::on>(listener);
     dispatcher.sink<message>().connect<&listener::on>(listener);
 
 
     cr_plugin ctx;
     cr_plugin ctx;
-    ctx.userdata = &dispatcher;
     cr_plugin_load(ctx, PLUGIN);
     cr_plugin_load(ctx, PLUGIN);
+
+    ctx.userdata = type_context::instance();
+    cr_plugin_update(ctx);
+
+    ctx.userdata = &dispatcher;
     cr_plugin_update(ctx);
     cr_plugin_update(ctx);
 
 
     ASSERT_EQ(listener.value, 42);
     ASSERT_EQ(listener.value, 42);

+ 15 - 3
test/lib/dispatcher_plugin/plugin.cpp

@@ -1,16 +1,28 @@
 #include <cr.h>
 #include <cr.h>
 #include <entt/core/type_info.hpp>
 #include <entt/core/type_info.hpp>
 #include <entt/signal/dispatcher.hpp>
 #include <entt/signal/dispatcher.hpp>
+#include "type_context.h"
 #include "types.h"
 #include "types.h"
 
 
+inline static type_context *context;
+
 template<typename Type>
 template<typename Type>
-struct entt::type_index<Type> {};
+struct entt::type_index<Type> {
+    [[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+        static const entt::id_type value = context->value(type_info<Type>::id());
+        return value;
+    }
+};
 
 
 CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
 CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
     switch (operation) {
     switch (operation) {
     case CR_STEP:
     case CR_STEP:
-        static_cast<entt::dispatcher *>(ctx->userdata)->trigger<event>();
-        static_cast<entt::dispatcher *>(ctx->userdata)->trigger<message>(42);
+        if(!context) {
+            context = static_cast<type_context *>(ctx->userdata);
+        } else {
+            static_cast<entt::dispatcher *>(ctx->userdata)->trigger<event>();
+            static_cast<entt::dispatcher *>(ctx->userdata)->trigger<message>(42);
+        }
         break;
         break;
     case CR_CLOSE:
     case CR_CLOSE:
     case CR_LOAD:
     case CR_LOAD:

+ 28 - 0
test/lib/dispatcher_plugin/type_context.h

@@ -0,0 +1,28 @@
+#ifndef ENTT_LIB_DISPATCHER_PLUGIN_TYPE_CONTEXT_H
+#define ENTT_LIB_DISPATCHER_PLUGIN_TYPE_CONTEXT_H
+
+#include <unordered_map>
+#include <entt/core/fwd.hpp>
+
+class type_context {
+    type_context() = default;
+
+public:
+    inline entt::id_type value(const entt::id_type name) {
+        if(name_to_index.find(name) == name_to_index.cend()) {
+            name_to_index[name] = entt::id_type(name_to_index.size());
+        }
+
+        return name_to_index[name];
+    }
+
+    static type_context * instance() {
+        static type_context self{};
+        return &self;
+    }
+
+private:
+    std::unordered_map<entt::id_type, entt::id_type> name_to_index{};
+};
+
+#endif

+ 12 - 2
test/lib/emitter_plugin/main.cpp

@@ -4,10 +4,16 @@
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <entt/core/type_info.hpp>
 #include <entt/core/type_info.hpp>
 #include <entt/signal/emitter.hpp>
 #include <entt/signal/emitter.hpp>
+#include "type_context.h"
 #include "types.h"
 #include "types.h"
 
 
 template<typename Type>
 template<typename Type>
-struct entt::type_index<Type> {};
+struct entt::type_index<Type> {
+    [[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+        static const entt::id_type value = type_context::instance()->value(entt::type_info<Type>::id());
+        return value;
+    }
+};
 
 
 TEST(Lib, Emitter) {
 TEST(Lib, Emitter) {
     test_emitter emitter;
     test_emitter emitter;
@@ -18,8 +24,12 @@ TEST(Lib, Emitter) {
     emitter.once<message>([&](message msg, test_emitter &) { value = msg.payload; });
     emitter.once<message>([&](message msg, test_emitter &) { value = msg.payload; });
 
 
     cr_plugin ctx;
     cr_plugin ctx;
-    ctx.userdata = &emitter;
     cr_plugin_load(ctx, PLUGIN);
     cr_plugin_load(ctx, PLUGIN);
+
+    ctx.userdata = type_context::instance();
+    cr_plugin_update(ctx);
+
+    ctx.userdata = &emitter;
     cr_plugin_update(ctx);
     cr_plugin_update(ctx);
 
 
     ASSERT_EQ(value, 42);
     ASSERT_EQ(value, 42);

+ 16 - 4
test/lib/emitter_plugin/plugin.cpp

@@ -1,17 +1,29 @@
 #include <cr.h>
 #include <cr.h>
 #include <entt/core/type_info.hpp>
 #include <entt/core/type_info.hpp>
 #include <entt/signal/emitter.hpp>
 #include <entt/signal/emitter.hpp>
+#include "type_context.h"
 #include "types.h"
 #include "types.h"
 
 
+inline static type_context *context;
+
 template<typename Type>
 template<typename Type>
-struct entt::type_index<Type> {};
+struct entt::type_index<Type> {
+    [[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+        static const entt::id_type value = context->value(type_info<Type>::id());
+        return value;
+    }
+};
 
 
 CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
 CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
     switch (operation) {
     switch (operation) {
     case CR_STEP:
     case CR_STEP:
-        static_cast<test_emitter *>(ctx->userdata)->publish<event>();
-        static_cast<test_emitter *>(ctx->userdata)->publish<message>(42);
-        static_cast<test_emitter *>(ctx->userdata)->publish<message>(3);
+        if(!context) {
+            context = static_cast<type_context *>(ctx->userdata);
+        } else {
+            static_cast<test_emitter *>(ctx->userdata)->publish<event>();
+            static_cast<test_emitter *>(ctx->userdata)->publish<message>(42);
+            static_cast<test_emitter *>(ctx->userdata)->publish<message>(3);
+        }
         break;
         break;
     case CR_CLOSE:
     case CR_CLOSE:
     case CR_LOAD:
     case CR_LOAD:

+ 28 - 0
test/lib/emitter_plugin/type_context.h

@@ -0,0 +1,28 @@
+#ifndef ENTT_LIB_EMITTER_PLUGIN_TYPE_CONTEXT_H
+#define ENTT_LIB_EMITTER_PLUGIN_TYPE_CONTEXT_H
+
+#include <unordered_map>
+#include <entt/core/fwd.hpp>
+
+class type_context {
+    type_context() = default;
+
+public:
+    inline entt::id_type value(const entt::id_type name) {
+        if(name_to_index.find(name) == name_to_index.cend()) {
+            name_to_index[name] = entt::id_type(name_to_index.size());
+        }
+
+        return name_to_index[name];
+    }
+
+    static type_context * instance() {
+        static type_context self{};
+        return &self;
+    }
+
+private:
+    std::unordered_map<entt::id_type, entt::id_type> name_to_index{};
+};
+
+#endif

+ 2 - 1
test/lib/meta_plugin/main.cpp

@@ -13,8 +13,9 @@ TEST(Lib, Meta) {
     userdata ud{};
     userdata ud{};
 
 
     cr_plugin ctx;
     cr_plugin ctx;
-    ctx.userdata = &ud;
     cr_plugin_load(ctx, PLUGIN);
     cr_plugin_load(ctx, PLUGIN);
+
+    ctx.userdata = &ud;
     cr_plugin_update(ctx);
     cr_plugin_update(ctx);
 
 
     entt::meta<double>().conv<int>();
     entt::meta<double>().conv<int>();

+ 2 - 1
test/lib/meta_plugin_std/main.cpp

@@ -13,8 +13,9 @@ TEST(Lib, Meta) {
     userdata ud{};
     userdata ud{};
 
 
     cr_plugin ctx;
     cr_plugin ctx;
-    ctx.userdata = &ud;
     cr_plugin_load(ctx, PLUGIN);
     cr_plugin_load(ctx, PLUGIN);
+
+    ctx.userdata = &ud;
     cr_plugin_update(ctx);
     cr_plugin_update(ctx);
 
 
     entt::meta<double>().conv<int>();
     entt::meta<double>().conv<int>();

+ 14 - 10
test/lib/registry_plugin/main.cpp

@@ -4,31 +4,35 @@
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <entt/core/type_info.hpp>
 #include <entt/core/type_info.hpp>
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
+#include "type_context.h"
 #include "types.h"
 #include "types.h"
 
 
 template<typename Type>
 template<typename Type>
-struct entt::type_index<Type> {};
+struct entt::type_index<Type> {
+    [[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+        static const entt::id_type value = type_context::instance()->value(entt::type_info<Type>::id());
+        return value;
+    }
+};
 
 
 TEST(Lib, Registry) {
 TEST(Lib, Registry) {
     entt::registry registry;
     entt::registry registry;
 
 
     for(auto i = 0; i < 3; ++i) {
     for(auto i = 0; i < 3; ++i) {
-        const auto entity = registry.create();
-        registry.emplace<position>(entity, i, i);
-
-        if(i % 2) {
-            registry.emplace<tag>(entity);
-        }
+        registry.emplace<position>(registry.create(), i, i);
     }
     }
 
 
     cr_plugin ctx;
     cr_plugin ctx;
-    ctx.userdata = &registry;
     cr_plugin_load(ctx, PLUGIN);
     cr_plugin_load(ctx, PLUGIN);
+
+    ctx.userdata = type_context::instance();
+    cr_plugin_update(ctx);
+
+    ctx.userdata = &registry;
     cr_plugin_update(ctx);
     cr_plugin_update(ctx);
 
 
     ASSERT_EQ(registry.size<position>(), registry.size<velocity>());
     ASSERT_EQ(registry.size<position>(), registry.size<velocity>());
-    ASSERT_NE(registry.size<position>(), registry.size());
-    ASSERT_TRUE(registry.empty<tag>());
+    ASSERT_EQ(registry.size<position>(), registry.size());
 
 
     registry.view<position>().each([](auto entity, auto &position) {
     registry.view<position>().each([](auto entity, auto &position) {
         ASSERT_EQ(position.x, static_cast<int>(entt::to_integral(entity) + 16u));
         ASSERT_EQ(position.x, static_cast<int>(entt::to_integral(entity) + 16u));

+ 20 - 12
test/lib/registry_plugin/plugin.cpp

@@ -1,28 +1,36 @@
 #include <cr.h>
 #include <cr.h>
 #include <entt/core/type_info.hpp>
 #include <entt/core/type_info.hpp>
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
+#include "type_context.h"
 #include "types.h"
 #include "types.h"
 
 
+inline static type_context *context;
+
 template<typename Type>
 template<typename Type>
-struct entt::type_index<Type> {};
+struct entt::type_index<Type> {
+    [[nodiscard]] static id_type value() ENTT_NOEXCEPT {
+        static const entt::id_type value = context->value(type_info<Type>::id());
+        return value;
+    }
+};
 
 
 CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
 CR_EXPORT int cr_main(cr_plugin *ctx, cr_op operation) {
     switch (operation) {
     switch (operation) {
     case CR_STEP:
     case CR_STEP:
-        [ctx]() {
-            auto &registry = *static_cast<entt::registry *>(ctx->userdata);
-
-            const auto position_view = registry.view<position>(entt::exclude<tag>);
-            registry.insert(position_view.begin(), position_view.end(), velocity{1., 1.});
-
-            registry.view<position, velocity>().each([](auto &pos, auto &vel) {
+        if(!context) {
+            context = static_cast<type_context *>(ctx->userdata);
+        } else {
+            // forces things to break
+            static_cast<entt::registry *>(ctx->userdata)->prepare<velocity>();
+            
+            const auto view = static_cast<entt::registry *>(ctx->userdata)->view<position>();
+            static_cast<entt::registry *>(ctx->userdata)->insert(view.begin(), view.end(), velocity{1., 1.});
+            
+            static_cast<entt::registry *>(ctx->userdata)->view<position, velocity>().each([](auto &pos, auto &vel) {
                 pos.x += static_cast<int>(16 * vel.dx);
                 pos.x += static_cast<int>(16 * vel.dx);
                 pos.y += static_cast<int>(16 * vel.dy);
                 pos.y += static_cast<int>(16 * vel.dy);
             });
             });
-
-            const auto tag_view = registry.view<tag>();
-            registry.destroy(tag_view.begin(), tag_view.end());
-        }();
+        }
         break;
         break;
     case CR_CLOSE:
     case CR_CLOSE:
     case CR_LOAD:
     case CR_LOAD:

+ 28 - 0
test/lib/registry_plugin/type_context.h

@@ -0,0 +1,28 @@
+#ifndef ENTT_LIB_REGISTRY_PLUGIN_TYPE_CONTEXT_H
+#define ENTT_LIB_REGISTRY_PLUGIN_TYPE_CONTEXT_H
+
+#include <unordered_map>
+#include <entt/core/fwd.hpp>
+
+class type_context {
+    type_context() = default;
+
+public:
+    inline entt::id_type value(const entt::id_type name) {
+        if(name_to_index.find(name) == name_to_index.cend()) {
+            name_to_index[name] = entt::id_type(name_to_index.size());
+        }
+
+        return name_to_index[name];
+    }
+
+    static type_context * instance() {
+        static type_context self{};
+        return &self;
+    }
+
+private:
+    std::unordered_map<entt::id_type, entt::id_type> name_to_index{};
+};
+
+#endif

+ 0 - 2
test/lib/registry_plugin/types.h

@@ -11,6 +11,4 @@ struct velocity {
     double dy;
     double dy;
 };
 };
 
 
-struct tag {};
-
 #endif
 #endif