diff --git a/CMakeLists.txt b/CMakeLists.txt
index fdc06641a..276767fa4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -187,6 +187,7 @@ if(BUILD_TESTING)
endif()
option(BUILD_BENCHMARK "Build benchmark." OFF)
+ option(BUILD_LIB "Build lib example." OFF)
option(BUILD_MOD "Build mod example." OFF)
option(BUILD_SNAPSHOT "Build snapshot example." OFF)
diff --git a/TODO b/TODO
index 00dce2f05..145ad72dd 100644
--- a/TODO
+++ b/TODO
@@ -7,7 +7,6 @@
* 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
* 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: move-to-head optimization when searching by name on parts (data, func, etc)
* 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
* allow to replace std:: with custom implementations
* allow to sort groups (::respect can already work with begin/end instead of a whole sparse set)
+<<<<<<< HEAD
+=======
+
+==> 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
diff --git a/appveyor.yml b/appveyor.yml
index ec525cd7d..03e6493e9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -14,7 +14,7 @@ configuration:
before_build:
- 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:
- ctest -C Release -j4
diff --git a/docs/core.md b/docs/core.md
index faaae89d3..f795dd90d 100644
--- a/docs/core.md
+++ b/docs/core.md
@@ -103,7 +103,7 @@ Indeed it mostly depends on the flow of execution.
# 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
counterparts at runtime, thus without affecting performance.
The class has an implicit `constexpr` constructor that chews a bunch of
@@ -138,7 +138,7 @@ possible. This is a fact.
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.
That's all.
-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
identifier is probably the best solution to make the conflict disappear in this
case.
diff --git a/src/entt/config/config.h b/src/entt/config/config.h
index b99640dfa..34802d12f 100644
--- a/src/entt/config/config.h
+++ b/src/entt/config/config.h
@@ -22,9 +22,9 @@ using maybe_atomic_t = Type;
#endif // ENTT_USE_ATOMIC
-#ifndef ENTT_HASH_TYPE
+#ifndef ENTT_ID_TYPE
#include
-#define ENTT_HASH_TYPE std::uint32_t
+#define ENTT_ID_TYPE std::uint32_t
#endif
diff --git a/src/entt/core/family.hpp b/src/entt/core/family.hpp
index fad9aebad..138ec05c7 100644
--- a/src/entt/core/family.hpp
+++ b/src/entt/core/family.hpp
@@ -3,7 +3,6 @@
#include
-#include
#include "../config/config.h"
@@ -19,14 +18,14 @@ namespace entt {
*/
template
class family {
- inline static maybe_atomic_t identifier;
+ inline static maybe_atomic_t identifier;
template
inline static const auto inner = identifier++;
public:
/*! @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. */
template
diff --git a/src/entt/core/hashed_string.hpp b/src/entt/core/hashed_string.hpp
index 2dd15f7f9..f552c87b2 100644
--- a/src/entt/core/hashed_string.hpp
+++ b/src/entt/core/hashed_string.hpp
@@ -56,7 +56,7 @@ struct fnv1a_traits {
* required.
*/
class hashed_string {
- using traits_type = internal::fnv1a_traits;
+ using traits_type = internal::fnv1a_traits;
struct const_wrapper {
// non-explicit constructor on purpose
@@ -65,13 +65,13 @@ class hashed_string {
};
// 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);
}
public:
/*! @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.
diff --git a/src/entt/core/ident.hpp b/src/entt/core/ident.hpp
index 472b064f2..8baee3428 100644
--- a/src/entt/core/ident.hpp
+++ b/src/entt/core/ident.hpp
@@ -3,7 +3,6 @@
#include
-#include
#include
#include
#include "../config/config.h"
@@ -44,14 +43,14 @@ class identifier {
using tuple_type = std::tuple...>;
template
- static constexpr std::size_t get(std::index_sequence) ENTT_NOEXCEPT {
+ static constexpr ENTT_ID_TYPE get(std::index_sequence) ENTT_NOEXCEPT {
static_assert(std::disjunction_v...>);
- return (0 + ... + (std::is_same_v> ? Indexes : std::size_t{}));
+ return (0 + ... + (std::is_same_v> ? ENTT_ID_TYPE(Indexes) : ENTT_ID_TYPE{}));
}
public:
/*! @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. */
template
diff --git a/src/entt/core/type_traits.hpp b/src/entt/core/type_traits.hpp
index c37716b80..9c1fdbe37 100644
--- a/src/entt/core/type_traits.hpp
+++ b/src/entt/core/type_traits.hpp
@@ -2,6 +2,10 @@
#define ENTT_CORE_TYPE_TRAITS_HPP
+#include
+#include "../core/hashed_string.hpp"
+
+
namespace entt {
@@ -34,6 +38,70 @@ constexpr auto type_list_cat(type_list, type_list, List...) {
}
+/*! @brief Traits class used mainly to push things across boundaries. */
+template
+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\
+ : std::integral_constant\
+ {}
+
+
+/**
+ * @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
+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
+struct is_shared>: std::true_type {};
+
+
+/**
+ * @brief Helper variable template.
+ *
+ * True if a given type is shared, false otherwise.
+ *
+ * @tparam Type Potentially shared type.
+ */
+template
+constexpr auto is_shared_v = is_shared::value;
+
+
}
diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp
index a6b208942..c185777b9 100644
--- a/src/entt/entity/registry.hpp
+++ b/src/entt/entity/registry.hpp
@@ -17,6 +17,7 @@
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "../core/family.hpp"
+#include "../core/hashed_string.hpp"
#include "../core/type_traits.hpp"
#include "../signal/sigh.hpp"
#include "entity.hpp"
@@ -63,9 +64,11 @@ class registry {
using signal_type = sigh;
using traits_type = entt_traits;
- struct signals {
+ struct pool_data {
+ std::unique_ptr> pool;
signal_type construction;
signal_type destruction;
+ ENTT_ID_TYPE runtime_type;
};
struct basic_descriptor {
@@ -85,17 +88,17 @@ class registry {
if(reg.has(entity) && (0 + ... + reg.has(entity)) == Accepted) {
const auto curr = data++;
(std::swap(reg.pool().get(entity), reg.pool().raw()[curr]), ...);
- (reg.pool().swap(reg.pools[reg.type()]->get(entity), curr), ...);
+ (reg.pool().swap(reg.pool().sparse_set::get(entity), curr), ...);
}
}
void discard_if(registry ®, const Entity entity) {
- const auto ctype = type>>();
+ auto &cpool = reg.pool>>();
- if(reg.pools[ctype]->has(entity) && reg.pools[ctype]->get(entity) < data) {
+ if(cpool.has(entity) && cpool.sparse_set::get(entity) < data) {
const auto curr = --data;
(std::swap(reg.pool().get(entity), reg.pool().raw()[curr]), ...);
- (reg.pool().swap(reg.pools[reg.type()]->get(entity), curr), ...);
+ (reg.pool().swap(reg.pool().sparse_set::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
inline const auto & pool() const ENTT_NOEXCEPT {
- assert(type() < pools.size() && pools[type()]);
- return static_cast> &>(*pools[type()]);
+ if constexpr(is_shared_v) {
+ const auto it = handler(type());
+ assert(it != pools.cend() && it->pool);
+ return static_cast> &>(it->pool);
+ } else {
+ const auto ctype = type();
+ assert(ctype < pools.size() && pools[ctype].pool && pools[ctype].runtime_type == ctype);
+ return static_cast> &>(*pools[ctype].pool);
+ }
}
template
@@ -139,16 +155,32 @@ class registry {
template
auto & assure() const {
- if(!(type() < pools.size())) {
- pools.resize(type()+1);
- sighs.resize(type()+1);
+ const auto ctype = type();
+ pool_data *pdata = nullptr;
+
+ if constexpr(is_shared_v) {
+ const auto it = handler(type());
+ 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()]) {
- pools[type()] = std::make_unique>>();
+ if(!pdata->pool) {
+ pdata->pool = std::make_unique>>();
+ pdata->runtime_type = ctype;
}
- return pool();
+ return static_cast> &>(*pdata->pool);
}
template
@@ -168,23 +200,6 @@ public:
/*! @brief Type of sink for the given component. */
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.
- * 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
- static component_type type() ENTT_NOEXCEPT {
- return component_family::type;
- }
-
/*! @brief Default constructor. */
registry() ENTT_NOEXCEPT = default;
@@ -198,6 +213,26 @@ public:
/*! @brief Default move assignment operator. @return This registry. */
registry & operator=(registry &&) = default;
+ /**
+ * @brief Returns the numeric identifier of a component.
+ *
+ * The given component doesn't need to be necessarily in use.
+ * 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
+ static component_type type() ENTT_NOEXCEPT {
+ if constexpr(is_shared_v) {
+ return shared_traits::value;
+ } else {
+ return component_family::type;
+ }
+ }
+
/**
* @brief Returns the number of existing components of the given type.
* @tparam Component Type of component of which to return the size.
@@ -511,11 +546,11 @@ public:
assert(valid(entity));
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) {
assert(valid(entity));
auto &component = assure().construct(entity, std::forward(args)...);
- sighs[type()].construction.publish(*this, entity);
+ handler(type())->construction.publish(*this, entity);
return component;
}
@@ -596,7 +631,7 @@ public:
template
void remove(const entity_type entity) {
assert(valid(entity));
- sighs[type()].destruction.publish(*this, entity);
+ handler(type())->destruction.publish(*this, entity);
pool().destroy(entity);
}
@@ -684,7 +719,7 @@ public:
if(!comp) {
comp = &cpool.construct(entity, std::forward(component));
- sighs[type()].construction.publish(*this, entity);
+ handler(type())->construction.publish(*this, entity);
}
return *comp;
@@ -783,7 +818,7 @@ public:
*comp = std::decay_t{std::forward(args)...};
} else {
comp = &cpool.construct(entity, std::forward(args)...);
- sighs[type()].construction.publish(*this, entity);
+ handler(type())->construction.publish(*this, entity);
}
return *comp;
@@ -815,7 +850,7 @@ public:
template
sink_type construction() ENTT_NOEXCEPT {
assure();
- return sighs[type()].construction.sink();
+ return handler(type())->construction.sink();
}
/**
@@ -844,7 +879,7 @@ public:
template
sink_type destruction() ENTT_NOEXCEPT {
assure();
- return sighs[type()].destruction.sink();
+ return handler(type())->destruction.sink();
}
/**
@@ -966,7 +1001,7 @@ public:
auto &cpool = assure();
if(cpool.has(entity)) {
- sighs[type()].destruction.publish(*this, entity);
+ handler(type())->destruction.publish(*this, entity);
cpool.destroy(entity);
}
}
@@ -982,7 +1017,7 @@ public:
template
void reset() {
auto &cpool = assure();
- auto &sigh = sighs[type()].destruction;
+ auto &sigh = handler(type())->destruction;
if(sigh.empty()) {
// no group set, otherwise the signal wouldn't be empty
@@ -1063,9 +1098,9 @@ public:
assert(valid(entity));
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;
@@ -1148,7 +1183,7 @@ public:
*/
template
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());
});
}
@@ -1200,27 +1235,35 @@ public:
assert((!owned() && ...));
groups[gtype] = std::make_unique();
auto *curr = static_cast(groups[gtype].get());
+ decltype(handler({})) it;
if constexpr(sizeof...(Owned) == 0) {
- ((sighs[type()].destruction.sink().template connect<&descriptor_type::destroy_if>(curr)), ...);
- ((sighs[type()].construction.sink().template connect<&descriptor_type::template construct_if<0>>(curr)), ...);
- ((sighs[type()].destruction.sink().template connect<&descriptor_type::template construct_if<1>>(curr)), ...);
- ((sighs[type()].construction.sink().template connect<&descriptor_type::destroy_if>(curr)), ...);
+ ((it = handler(type()),
+ 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()),
+ 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()) {
curr->template construct_if<0>(*this, entity);
}
} else {
- (sighs[type()].construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr), ...);
- (sighs[type()].construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr), ...);
+ ((it = handler(type()),
+ it->construction.sink().template connect<&descriptor_type::template induce_if<0>>(curr),
+ it->destruction.sink().template connect<&descriptor_type::discard_if>(curr)), ...);
- (sighs[type()].destruction.sink().template connect<&descriptor_type::discard_if>(curr), ...);
- (sighs[type()].destruction.sink().template connect<&descriptor_type::discard_if>(curr), ...);
+ ((it = handler(type()),
+ it->destruction.sink().template connect<&descriptor_type::template induce_if<1>>(curr),
+ it->construction.sink().template connect<&descriptor_type::discard_if>(curr)), ...);
- (sighs[type()].destruction.sink().template connect<&descriptor_type::template induce_if<1>>(curr), ...);
- (sighs[type()].construction.sink().template connect<&descriptor_type::discard_if>(curr), ...);
+ auto owned = {((it = handler(type()),
+ 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()].get()... }, [](const auto *lhs, const auto *rhs) {
+ const auto *cpool = std::min(owned, [](const auto *lhs, const auto *rhs) {
return lhs->size() < rhs->size();
});
@@ -1278,7 +1321,8 @@ public:
std::vector *> set(last - first);
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) };
@@ -1314,24 +1358,20 @@ public:
*/
template
registry clone() const {
+ static_assert(std::conjunction_v...>);
registry other;
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...>);
- ((other.pools[type()] = assure().clone()), ...);
- assert((other.pools[type()] && ...));
+ if(pdata.pool && (!sizeof...(Component) || ... || (pdata.runtime_type == type()))) {
+ auto &curr = other.pools[pos-1];
+ curr.pool = pdata.pool->clone();
+ curr.runtime_type = pdata.runtime_type;
+ assert(curr.pool);
+ }
}
other.next = next;
@@ -1409,8 +1449,7 @@ public:
private:
std::vector> groups;
- mutable std::vector>> pools;
- mutable std::vector sighs;
+ mutable std::vector pools;
std::vector entities;
size_type available{};
entity_type next{};
diff --git a/src/entt/process/process.hpp b/src/entt/process/process.hpp
index edd91e30d..5d4ded445 100644
--- a/src/entt/process/process.hpp
+++ b/src/entt/process/process.hpp
@@ -83,9 +83,6 @@ class process {
template
using state_value_t = std::integral_constant;
- template
- inline static state_value_t state_value{};
-
template
auto tick(int, state_value_t, void *data)
-> decltype(std::declval().init(data)) {
diff --git a/src/entt/signal/delegate.hpp b/src/entt/signal/delegate.hpp
index fd81fdf70..353f21994 100644
--- a/src/entt/signal/delegate.hpp
+++ b/src/entt/signal/delegate.hpp
@@ -54,7 +54,7 @@ struct connect_arg_t {};
/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
template
-inline static connect_arg_t connect_arg{};
+constexpr connect_arg_t connect_arg{};
/**
diff --git a/src/entt/signal/emitter.hpp b/src/entt/signal/emitter.hpp
index a4869e6a8..6cc16b6e5 100644
--- a/src/entt/signal/emitter.hpp
+++ b/src/entt/signal/emitter.hpp
@@ -6,7 +6,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -117,7 +116,7 @@ class emitter {
template
event_handler & handler() ENTT_NOEXCEPT {
- const std::size_t family = handler_family::type;
+ const auto family = handler_family::type;
if(!(family < handlers.size())) {
handlers.resize(family+1);
@@ -291,7 +290,7 @@ public:
*/
template
bool empty() const ENTT_NOEXCEPT {
- const std::size_t family = handler_family::type;
+ const auto family = handler_family::type;
return (!(family < handlers.size()) ||
!handlers[family] ||
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 553c2e196..0834ad2b1 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -5,12 +5,16 @@
include_directories($)
add_compile_options($)
+macro(SETUP_LIBRARY_TARGET LIB_TARGET)
+ set_target_properties(${LIB_TARGET} PROPERTIES CXX_EXTENSIONS OFF)
+ target_compile_definitions(${LIB_TARGET} PRIVATE $)
+ target_compile_features(${LIB_TARGET} PRIVATE $)
+ target_compile_options(${LIB_TARGET} PRIVATE $<$>:-pedantic -Wall>)
+ target_compile_options(${LIB_TARGET} PRIVATE $<$:/EHsc>)
+endmacro()
+
add_library(odr OBJECT odr.cpp)
-set_target_properties(odr PROPERTIES CXX_EXTENSIONS OFF)
-target_compile_definitions(odr PRIVATE $)
-target_compile_features(odr PRIVATE $)
-target_compile_options(odr PRIVATE $<$>:-pedantic -Wall>)
-target_compile_options(odr PRIVATE $<$:/EHsc>)
+SETUP_LIBRARY_TARGET(odr)
macro(SETUP_AND_ADD_TEST TEST_NAME TEST_SOURCE)
add_executable(${TEST_NAME} $ ${TEST_SOURCE})
@@ -29,6 +33,26 @@ if(BUILD_BENCHMARK)
SETUP_AND_ADD_TEST(benchmark benchmark/benchmark.cpp)
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
if(BUILD_MOD)
diff --git a/test/lib/a_module.cpp b/test/lib/a_module.cpp
new file mode 100644
index 000000000..7f3d51c6b
--- /dev/null
+++ b/test/lib/a_module.cpp
@@ -0,0 +1,29 @@
+#include
+
+#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();
+ (void)registry.type();
+
+ return registry.type();
+}
+
+LIB_EXPORT typename entt::registry<>::component_type a_module_char_type() {
+ entt::registry<> registry;
+
+ (void)registry.type();
+ (void)registry.type();
+
+ return registry.type();
+}
diff --git a/test/lib/another_module.cpp b/test/lib/another_module.cpp
new file mode 100644
index 000000000..d32a1fa74
--- /dev/null
+++ b/test/lib/another_module.cpp
@@ -0,0 +1,35 @@
+#include
+
+#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();
+ (void)registry.type();
+ (void)registry.type();
+ (void)registry.type();
+ (void)registry.type();
+
+ return registry.type();
+}
+
+LIB_EXPORT typename entt::registry<>::component_type another_module_char_type() {
+ entt::registry<> registry;
+
+ (void)registry.type();
+ (void)registry.type();
+ (void)registry.type();
+ (void)registry.type();
+ (void)registry.type();
+
+ return registry.type();
+}
diff --git a/test/lib/lib.cpp b/test/lib/lib.cpp
new file mode 100644
index 000000000..9905a16f2
--- /dev/null
+++ b/test/lib/lib.cpp
@@ -0,0 +1,24 @@
+#include
+#include
+
+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(), registry.type());
+ ASSERT_EQ(registry.type(), registry.type());
+
+ ASSERT_EQ(registry.type(), a_module_int_type());
+ ASSERT_EQ(registry.type(), a_module_char_type());
+ ASSERT_EQ(registry.type(), a_module_int_type());
+ ASSERT_EQ(registry.type(), a_module_char_type());
+
+ ASSERT_EQ(registry.type(), another_module_char_type());
+ ASSERT_EQ(registry.type(), another_module_int_type());
+ ASSERT_EQ(registry.type(), another_module_char_type());
+ ASSERT_EQ(registry.type(), another_module_int_type());
+}