Browse Source

wip: lib support

Michele Caini 7 years ago
parent
commit
617635a989

+ 1 - 0
CMakeLists.txt

@@ -187,6 +187,7 @@ if(BUILD_TESTING)
     endif()
     endif()
 
 
     option(BUILD_BENCHMARK "Build benchmark." OFF)
     option(BUILD_BENCHMARK "Build benchmark." OFF)
+    option(BUILD_LIB "Build lib example." OFF)
     option(BUILD_MOD "Build mod example." OFF)
     option(BUILD_MOD "Build mod example." OFF)
     option(BUILD_SNAPSHOT "Build snapshot example." OFF)
     option(BUILD_SNAPSHOT "Build snapshot example." OFF)
 
 

+ 11 - 1
TODO

@@ -7,7 +7,6 @@
 * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
 * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
 * runner proposal: https://en.wikipedia.org/wiki/Fork%E2%80%93join_model https://slide-rs.github.io/specs/03_dispatcher.html
 * runner proposal: https://en.wikipedia.org/wiki/Fork%E2%80%93join_model https://slide-rs.github.io/specs/03_dispatcher.html
 * work stealing job system (see #100)
 * work stealing job system (see #100)
-* can we do more for shared libraries? who knows... see #144
 * meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects
 * meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects
 * meta: move-to-head optimization when searching by name on parts (data, func, etc)
 * meta: move-to-head optimization when searching by name on parts (data, func, etc)
 * hashed string: add implicit check on construction for uniqueness (optional)
 * hashed string: add implicit check on construction for uniqueness (optional)
@@ -20,3 +19,14 @@
 * provide create with a pack of default constructible components to assign
 * provide create with a pack of default constructible components to assign
 * allow to replace std:: with custom implementations
 * allow to replace std:: with custom implementations
 * allow to sort groups (::respect can already work with begin/end instead of a whole sparse set)
 * allow to sort groups (::respect can already work with begin/end instead of a whole sparse set)
+<<<<<<< HEAD
+=======
+
+==> 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
appveyor.yml

@@ -14,7 +14,7 @@ configuration:
 
 
 before_build:
 before_build:
   - cd %BUILD_DIR%
   - cd %BUILD_DIR%
-  - cmake .. -DBUILD_TESTING=ON -DCMAKE_CXX_FLAGS=/W1 -G"Visual Studio 15 2017"
+  - cmake .. -DBUILD_TESTING=ON -DBUILD_LIB=ON -DCMAKE_CXX_FLAGS=/W1 -G"Visual Studio 15 2017"
 
 
 after_build:
 after_build:
   - ctest -C Release -j4
   - ctest -C Release -j4

+ 2 - 2
docs/core.md

@@ -103,7 +103,7 @@ Indeed it mostly depends on the flow of execution.
 
 
 # Hashed strings
 # Hashed strings
 
 
-A hashed string is a zero overhead resource identifier. Users can use
+A hashed string is a zero overhead unique identifier. Users can use
 human-readable identifiers in the codebase while using their numeric
 human-readable identifiers in the codebase while using their numeric
 counterparts at runtime, thus without affecting performance.<br/>
 counterparts at runtime, thus without affecting performance.<br/>
 The class has an implicit `constexpr` constructor that chews a bunch of
 The class has an implicit `constexpr` constructor that chews a bunch of
@@ -138,7 +138,7 @@ possible. This is a fact.<br/>
 There is no silver bullet to solve the problem of conflicts when dealing with
 There is no silver bullet to solve the problem of conflicts when dealing with
 hashing functions. In this case, the best solution seemed to be to give up.
 hashing functions. In this case, the best solution seemed to be to give up.
 That's all.<br/>
 That's all.<br/>
-After all, human-readable resource identifiers aren't something strictly defined
+After all, human-readable unique identifiers aren't something strictly defined
 and over which users have not the control. Choosing a slightly different
 and over which users have not the control. Choosing a slightly different
 identifier is probably the best solution to make the conflict disappear in this
 identifier is probably the best solution to make the conflict disappear in this
 case.
 case.

+ 2 - 2
src/entt/config/config.h

@@ -22,9 +22,9 @@ using maybe_atomic_t = Type;
 #endif // ENTT_USE_ATOMIC
 #endif // ENTT_USE_ATOMIC
 
 
 
 
-#ifndef ENTT_HASH_TYPE
+#ifndef ENTT_ID_TYPE
 #include <cstdint>
 #include <cstdint>
-#define ENTT_HASH_TYPE std::uint32_t
+#define ENTT_ID_TYPE std::uint32_t
 #endif
 #endif
 
 
 
 

+ 2 - 3
src/entt/core/family.hpp

@@ -3,7 +3,6 @@
 
 
 
 
 #include <type_traits>
 #include <type_traits>
-#include <cstddef>
 #include "../config/config.h"
 #include "../config/config.h"
 
 
 
 
@@ -19,14 +18,14 @@ namespace entt {
  */
  */
 template<typename...>
 template<typename...>
 class family {
 class family {
-    inline static maybe_atomic_t<std::size_t> identifier;
+    inline static maybe_atomic_t<ENTT_ID_TYPE> identifier;
 
 
     template<typename...>
     template<typename...>
     inline static const auto inner = identifier++;
     inline static const auto inner = identifier++;
 
 
 public:
 public:
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
-    using family_type = std::size_t;
+    using family_type = ENTT_ID_TYPE;
 
 
     /*! @brief Statically generated unique identifier for the given type. */
     /*! @brief Statically generated unique identifier for the given type. */
     template<typename... Type>
     template<typename... Type>

+ 3 - 3
src/entt/core/hashed_string.hpp

@@ -56,7 +56,7 @@ struct fnv1a_traits<std::uint64_t> {
  * required.
  * required.
  */
  */
 class hashed_string {
 class hashed_string {
-    using traits_type = internal::fnv1a_traits<ENTT_HASH_TYPE>;
+    using traits_type = internal::fnv1a_traits<ENTT_ID_TYPE>;
 
 
     struct const_wrapper {
     struct const_wrapper {
         // non-explicit constructor on purpose
         // non-explicit constructor on purpose
@@ -65,13 +65,13 @@ class hashed_string {
     };
     };
 
 
     // Fowler–Noll–Vo hash function v. 1a - the good
     // Fowler–Noll–Vo hash function v. 1a - the good
-    inline static constexpr ENTT_HASH_TYPE helper(ENTT_HASH_TYPE partial, const char *str) ENTT_NOEXCEPT {
+    inline static constexpr ENTT_ID_TYPE helper(ENTT_ID_TYPE partial, const char *str) ENTT_NOEXCEPT {
         return str[0] == 0 ? partial : helper((partial^str[0])*traits_type::prime, str+1);
         return str[0] == 0 ? partial : helper((partial^str[0])*traits_type::prime, str+1);
     }
     }
 
 
 public:
 public:
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
-    using hash_type = ENTT_HASH_TYPE;
+    using hash_type = ENTT_ID_TYPE;
 
 
     /**
     /**
      * @brief Returns directly the numeric representation of a string.
      * @brief Returns directly the numeric representation of a string.

+ 3 - 4
src/entt/core/ident.hpp

@@ -3,7 +3,6 @@
 
 
 
 
 #include <type_traits>
 #include <type_traits>
-#include <cstddef>
 #include <utility>
 #include <utility>
 #include <tuple>
 #include <tuple>
 #include "../config/config.h"
 #include "../config/config.h"
@@ -44,14 +43,14 @@ class identifier {
     using tuple_type = std::tuple<std::decay_t<Types>...>;
     using tuple_type = std::tuple<std::decay_t<Types>...>;
 
 
     template<typename Type, std::size_t... Indexes>
     template<typename Type, std::size_t... Indexes>
-    static constexpr std::size_t get(std::index_sequence<Indexes...>) ENTT_NOEXCEPT {
+    static constexpr ENTT_ID_TYPE get(std::index_sequence<Indexes...>) ENTT_NOEXCEPT {
         static_assert(std::disjunction_v<std::is_same<Type, Types>...>);
         static_assert(std::disjunction_v<std::is_same<Type, Types>...>);
-        return (0 + ... + (std::is_same_v<Type, std::tuple_element_t<Indexes, tuple_type>> ? Indexes : std::size_t{}));
+        return (0 + ... + (std::is_same_v<Type, std::tuple_element_t<Indexes, tuple_type>> ? ENTT_ID_TYPE(Indexes) : ENTT_ID_TYPE{}));
     }
     }
 
 
 public:
 public:
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
-    using identifier_type = std::size_t;
+    using identifier_type = ENTT_ID_TYPE;
 
 
     /*! @brief Statically generated unique identifier for the given type. */
     /*! @brief Statically generated unique identifier for the given type. */
     template<typename Type>
     template<typename Type>

+ 68 - 0
src/entt/core/type_traits.hpp

@@ -2,6 +2,10 @@
 #define ENTT_CORE_TYPE_TRAITS_HPP
 #define ENTT_CORE_TYPE_TRAITS_HPP
 
 
 
 
+#include <type_traits>
+#include "../core/hashed_string.hpp"
+
+
 namespace entt {
 namespace entt {
 
 
 
 
@@ -34,6 +38,70 @@ constexpr auto type_list_cat(type_list<Type...>, type_list<Other...>, List...) {
 }
 }
 
 
 
 
+/*! @brief Traits class used mainly to push things across boundaries. */
+template<typename>
+struct shared_traits;
+
+
+/**
+ * @brief Makes an already existing type a shared type.
+ * @param type Type to make shareable.
+ */
+#define ENTT_SHARED_TYPE(type)\
+    template<>\
+    struct shared_traits<type>\
+        : std::integral_constant<typename hashed_string::hash_type, hashed_string::to_value(#type)>\
+    {}
+
+
+/**
+ * @brief Defines a type as shareable (to use for structs).
+ * @param clazz Name of the type to make shareable.
+ */
+#define ENTT_SHARED_STRUCT(clazz)\
+    struct clazz;\
+    ENTT_SHARED_TYPE(clazz)\
+    struct clazz
+
+
+/**
+ * @brief Defines a type as shareable (to use for classes).
+ * @param clazz Name of the type to make shareable.
+ */
+#define ENTT_SHARED_CLASS(clazz)\
+    class clazz;\
+    ENTT_SHARED_TYPE(clazz)\
+    class clazz
+
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * shared. In all other cases, `value` is false.
+ */
+template<typename>
+struct is_shared: std::false_type {};
+
+
+/**
+ * @brief Provides the member constant `value` to true if a given type is
+ * shared. In all other cases, `value` is false.
+ * @tparam Type Potentially shared type.
+ */
+template<typename Type>
+struct is_shared<shared_traits<Type>>: std::true_type {};
+
+
+/**
+ * @brief Helper variable template.
+ *
+ * True if a given type is shared, false otherwise.
+ *
+ * @tparam Type Potentially shared type.
+ */
+template<class Type>
+constexpr auto is_shared_v = is_shared<Type>::value;
+
+
 }
 }
 
 
 
 

+ 112 - 73
src/entt/entity/registry.hpp

@@ -17,6 +17,7 @@
 #include "../config/config.h"
 #include "../config/config.h"
 #include "../core/algorithm.hpp"
 #include "../core/algorithm.hpp"
 #include "../core/family.hpp"
 #include "../core/family.hpp"
+#include "../core/hashed_string.hpp"
 #include "../core/type_traits.hpp"
 #include "../core/type_traits.hpp"
 #include "../signal/sigh.hpp"
 #include "../signal/sigh.hpp"
 #include "entity.hpp"
 #include "entity.hpp"
@@ -63,9 +64,11 @@ class registry {
     using signal_type = sigh<void(registry &, const Entity)>;
     using signal_type = sigh<void(registry &, const Entity)>;
     using traits_type = entt_traits<Entity>;
     using traits_type = entt_traits<Entity>;
 
 
-    struct signals {
+    struct pool_data {
+        std::unique_ptr<sparse_set<Entity>> pool;
         signal_type construction;
         signal_type construction;
         signal_type destruction;
         signal_type destruction;
+        ENTT_ID_TYPE runtime_type;
     };
     };
 
 
     struct basic_descriptor {
     struct basic_descriptor {
@@ -85,17 +88,17 @@ class registry {
             if(reg.has<Owned..., Get...>(entity) && (0 + ... + reg.has<Exclude>(entity)) == Accepted) {
             if(reg.has<Owned..., Get...>(entity) && (0 + ... + reg.has<Exclude>(entity)) == Accepted) {
                 const auto curr = data++;
                 const auto curr = data++;
                 (std::swap(reg.pool<Owned>().get(entity), reg.pool<Owned>().raw()[curr]), ...);
                 (std::swap(reg.pool<Owned>().get(entity), reg.pool<Owned>().raw()[curr]), ...);
-                (reg.pool<Owned>().swap(reg.pools[reg.type<Owned>()]->get(entity), curr), ...);
+                (reg.pool<Owned>().swap(reg.pool<Owned>().sparse_set<Entity>::get(entity), curr), ...);
             }
             }
         }
         }
 
 
         void discard_if(registry &reg, const Entity entity) {
         void discard_if(registry &reg, const Entity entity) {
-            const auto ctype = type<std::tuple_element_t<0, std::tuple<Owned...>>>();
+            auto &cpool = reg.pool<std::tuple_element_t<0, std::tuple<Owned...>>>();
 
 
-            if(reg.pools[ctype]->has(entity) && reg.pools[ctype]->get(entity) < data) {
+            if(cpool.has(entity) && cpool.sparse_set<Entity>::get(entity) < data) {
                 const auto curr = --data;
                 const auto curr = --data;
                 (std::swap(reg.pool<Owned>().get(entity), reg.pool<Owned>().raw()[curr]), ...);
                 (std::swap(reg.pool<Owned>().get(entity), reg.pool<Owned>().raw()[curr]), ...);
-                (reg.pool<Owned>().swap(reg.pools[reg.type<Owned>()]->get(entity), curr), ...);
+                (reg.pool<Owned>().swap(reg.pool<Owned>().sparse_set<Entity>::get(entity), curr), ...);
             }
             }
         }
         }
 
 
@@ -126,10 +129,23 @@ class registry {
         }
         }
     };
     };
 
 
+    inline auto handler(typename component_family::family_type ctype) const ENTT_NOEXCEPT {
+        return std::find_if(pools.begin(), pools.end(), [ctype](const auto &pdata) {
+            return pdata.pool && pdata.runtime_type == ctype;
+        });
+    }
+
     template<typename Component>
     template<typename Component>
     inline const auto & pool() const ENTT_NOEXCEPT {
     inline const auto & pool() const ENTT_NOEXCEPT {
-        assert(type<Component>() < pools.size() && pools[type<Component>()]);
-        return static_cast<const sparse_set<Entity, std::decay_t<Component>> &>(*pools[type<Component>()]);
+        if constexpr(is_shared_v<Component>) {
+            const auto it = handler(type<Component>());
+            assert(it != pools.cend() && 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);
+            return static_cast<const sparse_set<Entity, std::decay_t<Component>> &>(*pools[ctype].pool);
+        }
     }
     }
 
 
     template<typename Component>
     template<typename Component>
@@ -139,16 +155,32 @@ class registry {
 
 
     template<typename Component>
     template<typename Component>
     auto & assure() const {
     auto & assure() const {
-        if(!(type<Component>() < pools.size())) {
-            pools.resize(type<Component>()+1);
-            sighs.resize(type<Component>()+1);
+        const auto ctype = type<Component>();
+        pool_data *pdata = nullptr;
+
+        if constexpr(is_shared_v<Component>) {
+            const auto it = handler(type<Component>());
+            pdata = (it == pools.cend() ? &pools.emplace_back() : &(*it));
+        } else {
+            if(!(ctype < pools.size())) {
+                pools.resize(ctype+1);
+            }
+
+            pdata = &pools[ctype];
+
+            if(pdata->pool && pdata->runtime_type != ctype) {
+                pools.emplace_back();
+                std::swap(pools[ctype], pools.back());
+                pdata = &pools[ctype];
+            }
         }
         }
 
 
-        if(!pools[type<Component>()]) {
-            pools[type<Component>()] = std::make_unique<sparse_set<Entity, std::decay_t<Component>>>();
+        if(!pdata->pool) {
+            pdata->pool = std::make_unique<sparse_set<Entity, std::decay_t<Component>>>();
+            pdata->runtime_type = ctype;
         }
         }
 
 
-        return pool<Component>();
+        return static_cast<const sparse_set<Entity, std::decay_t<Component>> &>(*pdata->pool);
     }
     }
 
 
     template<typename Component>
     template<typename Component>
@@ -168,23 +200,6 @@ public:
     /*! @brief Type of sink for the given component. */
     /*! @brief Type of sink for the given component. */
     using sink_type = typename signal_type::sink_type;
     using sink_type = typename signal_type::sink_type;
 
 
-    /**
-     * @brief Returns the numeric identifier of a type of component at runtime.
-     *
-     * The given component doesn't need to be necessarily in use. However, the
-     * registry could decide to prepare internal data structures for it for
-     * later uses.<br/>
-     * Do not use this functionality to provide numeric identifiers to types at
-     * runtime.
-     *
-     * @tparam Component Type of component to query.
-     * @return Runtime numeric identifier of the given type of component.
-     */
-    template<typename Component>
-    static component_type type() ENTT_NOEXCEPT {
-        return component_family::type<Component>;
-    }
-
     /*! @brief Default constructor. */
     /*! @brief Default constructor. */
     registry() ENTT_NOEXCEPT = default;
     registry() ENTT_NOEXCEPT = default;
 
 
@@ -198,6 +213,26 @@ public:
     /*! @brief Default move assignment operator. @return This registry. */
     /*! @brief Default move assignment operator. @return This registry. */
     registry & operator=(registry &&) = default;
     registry & operator=(registry &&) = default;
 
 
+    /**
+     * @brief Returns the numeric identifier of a component.
+     *
+     * The given component doesn't need to be necessarily in use.<br/>
+     * Do not use this functionality to provide numeric identifiers to types at
+     * runtime, because they aren't guaranteed to be stable between different
+     * runs.
+     *
+     * @tparam Component Type of component to query.
+     * @return Runtime numeric identifier of the given type of component.
+     */
+    template<typename Component>
+    static component_type type() ENTT_NOEXCEPT {
+        if constexpr(is_shared_v<Component>) {
+            return shared_traits<Component>::value;
+        } else {
+            return component_family::type<Component>;
+        }
+    }
+
     /**
     /**
      * @brief Returns the number of existing components of the given type.
      * @brief Returns the number of existing components of the given type.
      * @tparam Component Type of component of which to return the size.
      * @tparam Component Type of component of which to return the size.
@@ -511,11 +546,11 @@ public:
         assert(valid(entity));
         assert(valid(entity));
 
 
         for(auto pos = pools.size(); pos; --pos) {
         for(auto pos = pools.size(); pos; --pos) {
-            auto &cpool = pools[pos-1];
+            auto &pdata = pools[pos-1];
 
 
-            if(cpool && cpool->has(entity)) {
-                sighs[pos-1].destruction.publish(*this, entity);
-                cpool->destroy(entity);
+            if(pdata.pool && pdata.pool->has(entity)) {
+                pools[pos-1].destruction.publish(*this, entity);
+                pdata.pool->destroy(entity);
             }
             }
         };
         };
 
 
@@ -576,7 +611,7 @@ public:
     Component & assign(const entity_type entity, Args &&... args) {
     Component & assign(const entity_type entity, Args &&... args) {
         assert(valid(entity));
         assert(valid(entity));
         auto &component = assure<Component>().construct(entity, std::forward<Args>(args)...);
         auto &component = assure<Component>().construct(entity, std::forward<Args>(args)...);
-        sighs[type<Component>()].construction.publish(*this, entity);
+        handler(type<Component>())->construction.publish(*this, entity);
         return component;
         return component;
     }
     }
 
 
@@ -596,7 +631,7 @@ public:
     template<typename Component>
     template<typename Component>
     void remove(const entity_type entity) {
     void remove(const entity_type entity) {
         assert(valid(entity));
         assert(valid(entity));
-        sighs[type<Component>()].destruction.publish(*this, entity);
+        handler(type<Component>())->destruction.publish(*this, entity);
         pool<Component>().destroy(entity);
         pool<Component>().destroy(entity);
     }
     }
 
 
@@ -684,7 +719,7 @@ public:
 
 
         if(!comp) {
         if(!comp) {
             comp = &cpool.construct(entity, std::forward<Component>(component));
             comp = &cpool.construct(entity, std::forward<Component>(component));
-            sighs[type<Component>()].construction.publish(*this, entity);
+            handler(type<Component>())->construction.publish(*this, entity);
         }
         }
 
 
         return *comp;
         return *comp;
@@ -783,7 +818,7 @@ public:
             *comp = std::decay_t<Component>{std::forward<Args>(args)...};
             *comp = std::decay_t<Component>{std::forward<Args>(args)...};
         } else {
         } else {
             comp = &cpool.construct(entity, std::forward<Args>(args)...);
             comp = &cpool.construct(entity, std::forward<Args>(args)...);
-            sighs[type<Component>()].construction.publish(*this, entity);
+            handler(type<Component>())->construction.publish(*this, entity);
         }
         }
 
 
         return *comp;
         return *comp;
@@ -815,7 +850,7 @@ public:
     template<typename Component>
     template<typename Component>
     sink_type construction() ENTT_NOEXCEPT {
     sink_type construction() ENTT_NOEXCEPT {
         assure<Component>();
         assure<Component>();
-        return sighs[type<Component>()].construction.sink();
+        return handler(type<Component>())->construction.sink();
     }
     }
 
 
     /**
     /**
@@ -844,7 +879,7 @@ public:
     template<typename Component>
     template<typename Component>
     sink_type destruction() ENTT_NOEXCEPT {
     sink_type destruction() ENTT_NOEXCEPT {
         assure<Component>();
         assure<Component>();
-        return sighs[type<Component>()].destruction.sink();
+        return handler(type<Component>())->destruction.sink();
     }
     }
 
 
     /**
     /**
@@ -966,7 +1001,7 @@ public:
         auto &cpool = assure<Component>();
         auto &cpool = assure<Component>();
 
 
         if(cpool.has(entity)) {
         if(cpool.has(entity)) {
-            sighs[type<Component>()].destruction.publish(*this, entity);
+            handler(type<Component>())->destruction.publish(*this, entity);
             cpool.destroy(entity);
             cpool.destroy(entity);
         }
         }
     }
     }
@@ -982,7 +1017,7 @@ public:
     template<typename Component>
     template<typename Component>
     void reset() {
     void reset() {
         auto &cpool = assure<Component>();
         auto &cpool = assure<Component>();
-        auto &sigh = sighs[type<Component>()].destruction;
+        auto &sigh = handler(type<Component>())->destruction;
 
 
         if(sigh.empty()) {
         if(sigh.empty()) {
             // no group set, otherwise the signal wouldn't be empty
             // no group set, otherwise the signal wouldn't be empty
@@ -1063,9 +1098,9 @@ public:
         assert(valid(entity));
         assert(valid(entity));
         bool orphan = true;
         bool orphan = true;
 
 
-        for(std::size_t i = 0; i < pools.size() && orphan; ++i) {
-            const auto &cpool = pools[i];
-            orphan = !(cpool && cpool->has(entity));
+        for(std::size_t i = {}; i < pools.size() && orphan; ++i) {
+            const auto &pdata = pools[i];
+            orphan = !(pdata.pool && pdata.pool->has(entity));
         }
         }
 
 
         return orphan;
         return orphan;
@@ -1148,7 +1183,7 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     bool owned() const ENTT_NOEXCEPT {
     bool owned() const ENTT_NOEXCEPT {
-        return std::any_of(groups.cbegin(), groups.cend(), [](const auto &curr) {
+        return std::any_of(groups.cbegin(), groups.cend(), [this](const auto &curr) {
             return curr && curr->owns(type<Component>());
             return curr && curr->owns(type<Component>());
         });
         });
     }
     }
@@ -1200,27 +1235,35 @@ public:
             assert((!owned<Owned>() && ...));
             assert((!owned<Owned>() && ...));
             groups[gtype] = std::make_unique<descriptor_type>();
             groups[gtype] = std::make_unique<descriptor_type>();
             auto *curr = static_cast<descriptor_type *>(groups[gtype].get());
             auto *curr = static_cast<descriptor_type *>(groups[gtype].get());
+            decltype(handler({})) it;
 
 
             if constexpr(sizeof...(Owned) == 0) {
             if constexpr(sizeof...(Owned) == 0) {
-                ((sighs[type<Get>()].destruction.sink().template connect<&descriptor_type::destroy_if>(curr)), ...);
-                ((sighs[type<Get>()].construction.sink().template connect<&descriptor_type::template construct_if<0>>(curr)), ...);
-                ((sighs[type<Exclude>()].destruction.sink().template connect<&descriptor_type::template construct_if<1>>(curr)), ...);
-                ((sighs[type<Exclude>()].construction.sink().template connect<&descriptor_type::destroy_if>(curr)), ...);
+                ((it = handler(type<Get>()),
+                        it->destruction.sink().template connect<&descriptor_type::destroy_if>(curr),
+                        it->construction.sink().template connect<&descriptor_type::template construct_if<0>>(curr)),
+                        ...);
+
+                ((it = handler(type<Exclude>()),
+                        it->destruction.sink().template connect<&descriptor_type::template construct_if<1>>(curr),
+                        it->construction.sink().template connect<&descriptor_type::destroy_if>(curr)), ...);
 
 
                 for(const auto entity: view<Get...>()) {
                 for(const auto entity: view<Get...>()) {
                     curr->template construct_if<0>(*this, entity);
                     curr->template construct_if<0>(*this, entity);
                 }
                 }
             } else {
             } else {
-                (sighs[type<Owned>()].construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr), ...);
-                (sighs[type<Get>()].construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr), ...);
+                ((it = handler(type<Get>()),
+                        it->construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr),
+                        it->destruction.sink().template connect<&descriptor_type::discard_if>(curr)), ...);
 
 
-                (sighs[type<Owned>()].destruction.sink().template connect<&descriptor_type::discard_if>(curr), ...);
-                (sighs[type<Get>()].destruction.sink().template connect<&descriptor_type::discard_if>(curr), ...);
+                ((it = handler(type<Exclude>()),
+                        it->destruction.sink().template connect<&descriptor_type::template induce_if<1>>(curr),
+                        it->construction.sink().template connect<&descriptor_type::discard_if>(curr)), ...);
 
 
-                (sighs[type<Exclude>()].destruction.sink().template connect<&descriptor_type::template induce_if<1>>(curr), ...);
-                (sighs[type<Exclude>()].construction.sink().template connect<&descriptor_type::discard_if>(curr), ...);
+                auto owned = {((it = handler(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()), ...)};
 
 
-                const auto *cpool = std::min({ pools[type<Owned>()].get()... }, [](const auto *lhs, const auto *rhs) {
+                const auto *cpool = std::min(owned, [](const auto *lhs, const auto *rhs) {
                     return lhs->size() < rhs->size();
                     return lhs->size() < rhs->size();
                 });
                 });
 
 
@@ -1278,7 +1321,8 @@ public:
         std::vector<const sparse_set<Entity> *> set(last - first);
         std::vector<const sparse_set<Entity> *> set(last - first);
 
 
         std::transform(first, last, set.begin(), [this](const component_type ctype) {
         std::transform(first, last, set.begin(), [this](const component_type ctype) {
-            return ctype < pools.size() ? pools[ctype].get() : nullptr;
+            auto pdata = handler(ctype);
+            return pdata != pools.cend() && pdata->pool ? pdata->pool.get() : nullptr;
         });
         });
 
 
         return { std::move(set) };
         return { std::move(set) };
@@ -1314,24 +1358,20 @@ public:
      */
      */
     template<typename... Component>
     template<typename... Component>
     registry clone() const {
     registry clone() const {
+        static_assert(std::conjunction_v<std::is_copy_constructible<Component>...>);
         registry other;
         registry other;
 
 
         other.pools.resize(pools.size());
         other.pools.resize(pools.size());
-        other.sighs.resize(pools.size());
 
 
-        if constexpr(sizeof...(Component) == 0) {
-            for(auto pos = pools.size(); pos; --pos) {
-                auto &cpool = pools[pos-1];
+        for(auto pos = pools.size(); pos; --pos) {
+            auto &pdata = pools[pos-1];
 
 
-                if(cpool) {
-                    other.pools[pos-1] = cpool->clone();
-                    assert(other.pools[pos-1]);
-                }
-            };
-        } else {
-            static_assert(std::conjunction_v<std::is_copy_constructible<Component>...>);
-            ((other.pools[type<Component>()] = assure<Component>().clone()), ...);
-            assert((other.pools[type<Component>()] && ...));
+            if(pdata.pool && (!sizeof...(Component) || ... || (pdata.runtime_type == type<Component>()))) {
+                auto &curr = other.pools[pos-1];
+                curr.pool = pdata.pool->clone();
+                curr.runtime_type = pdata.runtime_type;
+                assert(curr.pool);
+            }
         }
         }
 
 
         other.next = next;
         other.next = next;
@@ -1409,8 +1449,7 @@ public:
 
 
 private:
 private:
     std::vector<std::unique_ptr<basic_descriptor>> groups;
     std::vector<std::unique_ptr<basic_descriptor>> groups;
-    mutable std::vector<std::unique_ptr<sparse_set<Entity>>> pools;
-    mutable std::vector<signals> sighs;
+    mutable std::vector<pool_data> pools;
     std::vector<entity_type> entities;
     std::vector<entity_type> entities;
     size_type available{};
     size_type available{};
     entity_type next{};
     entity_type next{};

+ 0 - 3
src/entt/process/process.hpp

@@ -83,9 +83,6 @@ class process {
     template<state value>
     template<state value>
     using state_value_t = std::integral_constant<state, value>;
     using state_value_t = std::integral_constant<state, value>;
 
 
-    template<state value>
-    inline static state_value_t<value> state_value{};
-
     template<typename Target = Derived>
     template<typename Target = Derived>
     auto tick(int, state_value_t<state::UNINITIALIZED>, void *data)
     auto tick(int, state_value_t<state::UNINITIALIZED>, void *data)
     -> decltype(std::declval<Target>().init(data)) {
     -> decltype(std::declval<Target>().init(data)) {

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

@@ -54,7 +54,7 @@ struct connect_arg_t {};
 
 
 /*! @brief Constant of type connect_arg_t used to disambiguate calls. */
 /*! @brief Constant of type connect_arg_t used to disambiguate calls. */
 template<auto Func>
 template<auto Func>
-inline static connect_arg_t<Func> connect_arg{};
+constexpr connect_arg_t<Func> connect_arg{};
 
 
 
 
 /**
 /**

+ 2 - 3
src/entt/signal/emitter.hpp

@@ -6,7 +6,6 @@
 #include <functional>
 #include <functional>
 #include <algorithm>
 #include <algorithm>
 #include <utility>
 #include <utility>
-#include <cstddef>
 #include <memory>
 #include <memory>
 #include <vector>
 #include <vector>
 #include <list>
 #include <list>
@@ -117,7 +116,7 @@ class emitter {
 
 
     template<typename Event>
     template<typename Event>
     event_handler<Event> & handler() ENTT_NOEXCEPT {
     event_handler<Event> & handler() ENTT_NOEXCEPT {
-        const std::size_t family = handler_family::type<Event>;
+        const auto family = handler_family::type<Event>;
 
 
         if(!(family < handlers.size())) {
         if(!(family < handlers.size())) {
             handlers.resize(family+1);
             handlers.resize(family+1);
@@ -291,7 +290,7 @@ public:
      */
      */
     template<typename Event>
     template<typename Event>
     bool empty() const ENTT_NOEXCEPT {
     bool empty() const ENTT_NOEXCEPT {
-        const std::size_t family = handler_family::type<Event>;
+        const auto family = handler_family::type<Event>;
 
 
         return (!(family < handlers.size()) ||
         return (!(family < handlers.size()) ||
                 !handlers[family] ||
                 !handlers[family] ||

+ 29 - 5
test/CMakeLists.txt

@@ -5,12 +5,16 @@
 include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
 include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
 add_compile_options($<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_OPTIONS>)
 add_compile_options($<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_OPTIONS>)
 
 
+macro(SETUP_LIBRARY_TARGET LIB_TARGET)
+    set_target_properties(${LIB_TARGET} PROPERTIES CXX_EXTENSIONS OFF)
+    target_compile_definitions(${LIB_TARGET} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
+    target_compile_features(${LIB_TARGET} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
+    target_compile_options(${LIB_TARGET} PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
+    target_compile_options(${LIB_TARGET} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc>)
+endmacro()
+
 add_library(odr OBJECT odr.cpp)
 add_library(odr OBJECT odr.cpp)
-set_target_properties(odr PROPERTIES CXX_EXTENSIONS OFF)
-target_compile_definitions(odr PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
-target_compile_features(odr PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
-target_compile_options(odr PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
-target_compile_options(odr PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc>)
+SETUP_LIBRARY_TARGET(odr)
 
 
 macro(SETUP_AND_ADD_TEST TEST_NAME TEST_SOURCE)
 macro(SETUP_AND_ADD_TEST TEST_NAME TEST_SOURCE)
     add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCE})
     add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCE})
@@ -29,6 +33,26 @@ if(BUILD_BENCHMARK)
     SETUP_AND_ADD_TEST(benchmark benchmark/benchmark.cpp)
     SETUP_AND_ADD_TEST(benchmark benchmark/benchmark.cpp)
 endif()
 endif()
 
 
+# Test lib
+
+if(BUILD_LIB)
+    add_library(a_module_shared SHARED lib/a_module.cpp)
+    add_library(a_module_static STATIC lib/a_module.cpp)
+    add_library(another_module_shared SHARED lib/another_module.cpp)
+    add_library(another_module_static STATIC lib/another_module.cpp)
+
+    SETUP_LIBRARY_TARGET(a_module_shared)
+    SETUP_LIBRARY_TARGET(a_module_static)
+    SETUP_LIBRARY_TARGET(another_module_shared)
+    SETUP_LIBRARY_TARGET(another_module_static)
+
+    SETUP_AND_ADD_TEST(lib_shared lib/lib.cpp)
+    target_link_libraries(lib_shared PRIVATE a_module_shared another_module_shared)
+
+    SETUP_AND_ADD_TEST(lib_static lib/lib.cpp)
+    target_link_libraries(lib_static PRIVATE a_module_static another_module_static)
+endif()
+
 # Test mod
 # Test mod
 
 
 if(BUILD_MOD)
 if(BUILD_MOD)

+ 29 - 0
test/lib/a_module.cpp

@@ -0,0 +1,29 @@
+#include <entt/entity/registry.hpp>
+
+#ifndef LIB_EXPORT
+#if defined _WIN32 || defined __CYGWIN__
+#define LIB_EXPORT __declspec(dllexport)
+#elif defined __GNUC__
+#define LIB_EXPORT __attribute__((visibility("default")))
+#else
+#define LIB_EXPORT
+#endif
+#endif
+
+LIB_EXPORT typename entt::registry<>::component_type a_module_int_type() {
+    entt::registry<> registry;
+
+    (void)registry.type<double>();
+    (void)registry.type<float>();
+
+    return registry.type<int>();
+}
+
+LIB_EXPORT typename entt::registry<>::component_type a_module_char_type() {
+    entt::registry<> registry;
+
+    (void)registry.type<double>();
+    (void)registry.type<float>();
+
+    return registry.type<char>();
+}

+ 35 - 0
test/lib/another_module.cpp

@@ -0,0 +1,35 @@
+#include <entt/entity/registry.hpp>
+
+#ifndef LIB_EXPORT
+#if defined _WIN32 || defined __CYGWIN__
+#define LIB_EXPORT __declspec(dllexport)
+#elif defined __GNUC__
+#define LIB_EXPORT __attribute__((visibility("default")))
+#else
+#define LIB_EXPORT
+#endif
+#endif
+
+LIB_EXPORT typename entt::registry<>::component_type another_module_int_type() {
+    entt::registry<> registry;
+
+    (void)registry.type<char>();
+    (void)registry.type<const int>();
+    (void)registry.type<double>();
+    (void)registry.type<const char>();
+    (void)registry.type<float>();
+
+    return registry.type<int>();
+}
+
+LIB_EXPORT typename entt::registry<>::component_type another_module_char_type() {
+    entt::registry<> registry;
+
+    (void)registry.type<int>();
+    (void)registry.type<const char>();
+    (void)registry.type<float>();
+    (void)registry.type<const int>();
+    (void)registry.type<double>();
+
+    return registry.type<char>();
+}

+ 24 - 0
test/lib/lib.cpp

@@ -0,0 +1,24 @@
+#include <entt/entity/registry.hpp>
+#include <gtest/gtest.h>
+
+extern typename entt::registry<>::component_type a_module_int_type();
+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();
+
+TEST(Lib, Shared) {
+    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>(), a_module_int_type());
+    ASSERT_EQ(registry.type<char>(), a_module_char_type());
+    ASSERT_EQ(registry.type<const int>(), a_module_int_type());
+    ASSERT_EQ(registry.type<const char>(), a_module_char_type());
+
+    ASSERT_EQ(registry.type<const char>(), another_module_char_type());
+    ASSERT_EQ(registry.type<const int>(), another_module_int_type());
+    ASSERT_EQ(registry.type<char>(), another_module_char_type());
+    ASSERT_EQ(registry.type<int>(), another_module_int_type());
+}