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()); +}