diff --git a/src/entt/meta/factory.hpp b/src/entt/meta/factory.hpp index deda88292..4c49bc69a 100644 --- a/src/entt/meta/factory.hpp +++ b/src/entt/meta/factory.hpp @@ -19,6 +19,27 @@ namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +void meta_details_setup(Type &value) { + if(!value.details) { + value.details = std::make_shared(); + } +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + /** * @brief Meta factory to be used for reflection purposes. * @@ -33,17 +54,17 @@ class meta_factory; /** * @brief Extended meta factory to be used for reflection purposes. * @tparam Type Reflected type for which the factory was created. - * @tparam Prop Type of container to store properties. + * @tparam Node Node for which to create properties, if any. */ -template -class meta_factory: public meta_factory { +template +class meta_factory: public meta_factory { public: /** * @brief Constructs an extended factory from a given node. - * @param target The underlying container to store properties. + * @param target The node for which to store properties, if any. */ - meta_factory(Prop &target) noexcept - : container{&target} {} + meta_factory(Node &target) noexcept + : node{&target} {} /** * @brief Assigns a property to the last meta object created. @@ -57,7 +78,9 @@ public: */ template meta_factory prop(id_type key, Value &&...value) { - (*container)[key] = internal::meta_prop_node{ + internal::meta_details_setup(*node); + + node->details->prop[key] = internal::meta_prop_node{ &internal::resolve>..., std::forward(value)...}; @@ -65,7 +88,7 @@ public: } private: - Prop *container; + Node *node; }; /** @@ -89,8 +112,9 @@ class meta_factory { +[](meta_handle instance, meta_any value) -> bool { return (meta_setter>(*instance.operator->(), value.as_ref()) || ...); }, &meta_getter}; - auto it = owner->data.insert_or_assign(id, std::move(node)).first; - return meta_factory{it->second.prop}; + internal::meta_details_setup(*owner); + auto it = owner->details->data.insert_or_assign(id, std::move(node)).first; + return meta_factory{it->second}; } public: @@ -106,7 +130,7 @@ public: auto type(const id_type id) noexcept { ENTT_ASSERT(owner->id == id || !resolve(id), "Duplicate identifier"); owner->id = id; - return meta_factory{owner->prop}; + return meta_factory{*owner}; } /** @@ -120,8 +144,9 @@ public: template auto base() noexcept { static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); + internal::meta_details_setup(*owner); - owner->base[type_id().hash()] = internal::meta_base_node{ + owner->details->base[type_id().hash()] = internal::meta_base_node{ &internal::resolve, +[](meta_any other) noexcept -> meta_any { if(auto *ptr = other.data(); ptr) { @@ -149,8 +174,9 @@ public: template auto conv() noexcept { using conv_type = std::remove_cv_t>>; + internal::meta_details_setup(*owner); - owner->conv[type_id().hash()] = internal::meta_conv_node{ + owner->details->conv[type_id().hash()] = internal::meta_conv_node{ +[](const meta_any &instance) -> meta_any { return forward_as_meta(std::invoke(Candidate, *static_cast(instance.data()))); }}; @@ -170,8 +196,9 @@ public: template auto conv() noexcept { using conv_type = std::remove_cv_t>; + internal::meta_details_setup(*owner); - owner->conv[type_id().hash()] = internal::meta_conv_node{ + owner->details->conv[type_id().hash()] = internal::meta_conv_node{ +[](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast(*static_cast(instance.data()))); }}; @@ -197,8 +224,9 @@ public: using descriptor = meta_function_helper_t; static_assert(Policy::template value, "Invalid return type for the given policy"); static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); + internal::meta_details_setup(*owner); - owner->ctor[type_id().hash()] = internal::meta_ctor_node{ + owner->details->ctor[type_id().hash()] = internal::meta_ctor_node{ descriptor::args_type::size, &meta_arg, &meta_construct}; @@ -219,8 +247,9 @@ public: template auto ctor() noexcept { using descriptor = meta_function_helper_t; + internal::meta_details_setup(*owner); - owner->ctor[type_id().hash()] = internal::meta_ctor_node{ + owner->details->ctor[type_id().hash()] = internal::meta_ctor_node{ descriptor::args_type::size, &meta_arg, &meta_construct}; @@ -268,6 +297,8 @@ public: */ template auto data(const id_type id) noexcept { + internal::meta_details_setup(*owner); + if constexpr(std::is_member_object_pointer_v) { using data_type = std::remove_reference_t>; @@ -280,8 +311,8 @@ public: &meta_setter, &meta_getter}; - auto it = owner->data.insert_or_assign(id, std::move(node)).first; - return meta_factory{it->second.prop}; + auto it = owner->details->data.insert_or_assign(id, std::move(node)).first; + return meta_factory{it->second}; } else { using data_type = std::remove_reference_t>; @@ -293,8 +324,8 @@ public: &meta_setter, &meta_getter}; - auto it = owner->data.insert_or_assign(id, std::move(node)).first; - return meta_factory{it->second.prop}; + auto it = owner->details->data.insert_or_assign(id, std::move(node)).first; + return meta_factory{it->second}; } } @@ -322,6 +353,7 @@ public: auto data(const id_type id) noexcept { using data_type = std::invoke_result_t; static_assert(Policy::template value, "Invalid return type for the given policy"); + internal::meta_details_setup(*owner); if constexpr(std::is_same_v) { internal::meta_data_node node{ @@ -333,8 +365,8 @@ public: &meta_setter, &meta_getter}; - auto it = owner->data.insert_or_assign(id, std::move(node)).first; - return meta_factory{it->second.prop}; + auto it = owner->details->data.insert_or_assign(id, std::move(node)).first; + return meta_factory{it->second}; } else { using args_type = typename meta_function_helper_t::args_type; @@ -347,8 +379,8 @@ public: &meta_setter, &meta_getter}; - auto it = owner->data.insert_or_assign(id, std::move(node)).first; - return meta_factory{it->second.prop}; + auto it = owner->details->data.insert_or_assign(id, std::move(node)).first; + return meta_factory{it->second}; } } @@ -391,6 +423,7 @@ public: auto func(const id_type id) noexcept { using descriptor = meta_function_helper_t; static_assert(Policy::template value, "Invalid return type for the given policy"); + internal::meta_details_setup(*owner); internal::meta_func_node node{ (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), @@ -399,22 +432,22 @@ public: &meta_arg, &meta_invoke}; - if(auto it = owner->func.find(id); it != owner->func.end()) { + if(auto it = owner->details->func.find(id); it != owner->details->func.end()) { for(auto *curr = &it->second; curr; curr = curr->next.get()) { if(curr->invoke == node.invoke) { node.next = std::move(curr->next); *curr = std::move(node); - return meta_factory{curr->prop}; + return meta_factory{*curr}; } } // locally overloaded function - node.next = std::make_unique(std::move(owner->func[id])); + node.next = std::make_shared(std::move(owner->details->func[id])); } - owner->func.insert_or_assign(id, std::move(node)); - return meta_factory{owner->func[id].prop}; + owner->details->func.insert_or_assign(id, std::move(node)); + return meta_factory{owner->details->func[id]}; } private: @@ -436,7 +469,7 @@ template [[nodiscard]] auto meta() noexcept { auto *const node = internal::resolve(); // extended meta factory to allow assigning properties to opaque meta types - return meta_factory{node->prop}; + return meta_factory{*node}; } /** diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index f149a77b5..6437168c4 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -343,8 +343,8 @@ public: [[nodiscard]] const Type *try_cast() const { auto *self = any_cast(&storage); - if(!self && node) { - for(auto it = node->base.cbegin(), last = node->base.cend(); it != last && !self; ++it) { + if(!self && node && node->details) { + for(auto it = node->details->base.cbegin(), last = node->details->base.cend(); it != last && !self; ++it) { const auto &as_const = it->second.cast(as_ref()); self = as_const.template try_cast(); } @@ -358,8 +358,8 @@ public: [[nodiscard]] Type *try_cast() { auto *self = any_cast(&storage); - if(!self && node) { - for(auto it = node->base.cbegin(), last = node->base.cend(); it != last && !self; ++it) { + if(!self && node && node->details) { + for(auto it = node->details->base.cbegin(), last = node->details->base.cend(); it != last && !self; ++it) { self = it->second.cast(as_ref()).template try_cast(); } } @@ -764,8 +764,12 @@ struct meta_data { * @brief Returns a range to visit registered meta properties. * @return An iterable range to visit registered meta properties. */ - [[nodiscard]] meta_range prop() const noexcept { - return {node->prop.cbegin(), node->prop.cend()}; + [[nodiscard]] meta_range prop() const noexcept { + if(node->details) { + return {node->details->prop.cbegin(), node->details->prop.cend()}; + } + + return {}; } /** @@ -774,8 +778,10 @@ struct meta_data { * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { - if(auto it = node->prop.find(key); it != node->prop.cend()) { - return &it->second; + if(node->details) { + if(auto it = node->details->prop.find(key); it != node->details->prop.cend()) { + return &it->second; + } } return nullptr; @@ -876,8 +882,12 @@ struct meta_func { } /*! @copydoc meta_data::prop */ - [[nodiscard]] meta_range prop() const noexcept { - return {node->prop.cbegin(), node->prop.cend()}; + [[nodiscard]] meta_range prop() const noexcept { + if(node->details) { + return {node->details->prop.cbegin(), node->details->prop.cend()}; + } + + return {}; } /** @@ -886,8 +896,10 @@ struct meta_func { * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { - if(auto it = node->prop.find(key); it != node->prop.cend()) { - return &it->second; + if(node->details) { + if(auto it = node->details->prop.find(key); it != node->details->prop.cend()) { + return &it->second; + } } return nullptr; @@ -935,7 +947,7 @@ class meta_type { if(const auto &info = other.info(); info == type.info()) { ++direct; } else { - ext += type.node->base.contains(info.hash()) || type.node->conv.contains(info.hash()) + ext += (type.node->details && (type.node->details->base.contains(info.hash()) || type.node->details->conv.contains(info.hash()))) || (type.node->conversion_helper && other.node->conversion_helper); } } @@ -1131,16 +1143,24 @@ public: * @brief Returns a range to visit registered top-level base meta types. * @return An iterable range to visit registered top-level base meta types. */ - [[nodiscard]] meta_range base() const noexcept { - return {node->base.cbegin(), node->base.cend()}; + [[nodiscard]] meta_range base() const noexcept { + if(node->details) { + return {node->details->base.cbegin(), node->details->base.cend()}; + } + + return {}; } /** * @brief Returns a range to visit registered top-level meta data. * @return An iterable range to visit registered top-level meta data. */ - [[nodiscard]] meta_range data() const noexcept { - return {node->data.cbegin(), node->data.cend()}; + [[nodiscard]] meta_range data() const noexcept { + if(node->details) { + return {node->details->data.cbegin(), node->details->data.cend()}; + } + + return {}; } /** @@ -1152,8 +1172,10 @@ public: * @return The registered meta data for the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { - if(auto it = node->data.find(id); it != node->data.cend()) { - return &it->second; + if(node->details) { + if(auto it = node->details->data.find(id); it != node->details->data.cend()) { + return &it->second; + } } for(auto &&curr: base()) { @@ -1169,8 +1191,12 @@ public: * @brief Returns a range to visit registered top-level functions. * @return An iterable range to visit registered top-level functions. */ - [[nodiscard]] meta_range func() const noexcept { - return {node->func.cbegin(), node->func.cend()}; + [[nodiscard]] meta_range func() const noexcept { + if(node->details) { + return {node->details->func.cbegin(), node->details->func.cend()}; + } + + return {}; } /** @@ -1184,8 +1210,10 @@ public: * @return The registered meta function for the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { - if(auto it = node->func.find(id); it != node->func.cend()) { - return &it->second; + if(node->details) { + if(auto it = node->details->func.find(id); it != node->details->func.cend()) { + return &it->second; + } } for(auto &&curr: base()) { @@ -1209,15 +1237,21 @@ public: * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { - const auto *candidate = lookup(args, sz, [first = node->ctor.cbegin(), last = node->ctor.cend()]() mutable { - return first == last ? nullptr : &(first++)->second; - }); + if(node->details) { + const auto *candidate = lookup(args, sz, [first = node->details->ctor.cbegin(), last = node->details->ctor.cend()]() mutable { + return first == last ? nullptr : &(first++)->second; + }); - if(candidate) { - return candidate->invoke(args); + if(candidate) { + return candidate->invoke(args); + } } - return (sz == 0u && node->default_constructor) ? node->default_constructor() : meta_any{}; + if(sz == 0u && node->default_constructor) { + return node->default_constructor(); + } + + return meta_any{}; } /** @@ -1264,13 +1298,15 @@ public: * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { - if(auto it = node->func.find(id); it != node->func.cend()) { - const auto *candidate = lookup(args, sz, [curr = &it->second]() mutable { - return curr ? std::exchange(curr, curr->next.get()) : nullptr; - }); + if(node->details) { + if(auto it = node->details->func.find(id); it != node->details->func.cend()) { + const auto *candidate = lookup(args, sz, [curr = &it->second]() mutable { + return curr ? std::exchange(curr, curr->next.get()) : nullptr; + }); - if(candidate) { - return candidate->invoke(std::move(instance), args); + if(candidate) { + return candidate->invoke(std::move(instance), args); + } } } @@ -1339,8 +1375,12 @@ public: * @brief Returns a range to visit registered top-level meta properties. * @return An iterable range to visit registered top-level meta properties. */ - [[nodiscard]] meta_range prop() const noexcept { - return {node->prop.cbegin(), node->prop.cend()}; + [[nodiscard]] meta_range prop() const noexcept { + if(node->details) { + return {node->details->prop.cbegin(), node->details->prop.cend()}; + } + + return {}; } /** @@ -1352,8 +1392,10 @@ public: * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { - if(auto it = node->prop.find(key); it != node->prop.cend()) { - return &it->second; + if(node->details) { + if(auto it = node->details->prop.find(key); it != node->details->prop.cend()) { + return &it->second; + } } for(auto &&curr: base()) { @@ -1427,8 +1469,10 @@ bool meta_any::set(const id_type id, Type &&value) { if(const auto &info = type.info(); node && *node->info == info) { return as_ref(); } else if(node) { - if(auto it = node->conv.find(info.hash()); it != node->conv.cend()) { - return it->second.conv(*this); + if(node->details) { + if(auto it = node->details->conv.find(info.hash()); it != node->details->conv.cend()) { + return it->second.conv(*this); + } } if(node->conversion_helper && (type.is_arithmetic() || type.is_enum())) { @@ -1440,11 +1484,13 @@ bool meta_any::set(const id_type id, Type &&value) { return other; } - for(auto &&curr: node->base) { - const auto &as_const = curr.second.cast(as_ref()); + if(node->details) { + for(auto &&curr: node->details->base) { + const auto &as_const = curr.second.cast(as_ref()); - if(auto other = as_const.allow_cast(type); other) { - return other; + if(auto other = as_const.allow_cast(type); other) { + return other; + } } } } diff --git a/src/entt/meta/node.hpp b/src/entt/meta/node.hpp index 1b00a47e1..642d95826 100644 --- a/src/entt/meta/node.hpp +++ b/src/entt/meta/node.hpp @@ -76,25 +76,33 @@ struct meta_dtor_node { struct meta_data_node { using size_type = std::size_t; + struct cold_data_t { + dense_map prop; + }; + meta_traits traits{meta_traits::is_none}; size_type arity{0u}; meta_type_node *(*type)() noexcept {nullptr}; meta_type (*arg)(const size_type) noexcept {nullptr}; bool (*set)(meta_handle, meta_any){nullptr}; meta_any (*get)(meta_handle){nullptr}; - dense_map prop{}; + std::shared_ptr details{}; }; struct meta_func_node { using size_type = std::size_t; + struct cold_data_t { + dense_map prop; + }; + meta_traits traits{meta_traits::is_none}; size_type arity{0u}; meta_type_node *(*ret)() noexcept {nullptr}; meta_type (*arg)(const size_type) noexcept {nullptr}; meta_any (*invoke)(meta_handle, meta_any *const){nullptr}; - dense_map prop{}; - std::unique_ptr next{}; + std::shared_ptr next{}; + std::shared_ptr details{}; }; struct meta_template_node { @@ -108,6 +116,15 @@ struct meta_template_node { struct meta_type_node { using size_type = std::size_t; + struct cold_data_t { + dense_map prop{}; + dense_map ctor{}; + dense_map base{}; + dense_map conv{}; + dense_map data{}; + dense_map func{}; + }; + const type_info *info{nullptr}; id_type id{}; meta_traits traits{meta_traits::is_none}; @@ -117,13 +134,8 @@ struct meta_type_node { double (*conversion_helper)(void *, const void *){nullptr}; meta_any (*from_void)(void *, const void *){nullptr}; meta_template_node templ{}; - dense_map prop{}; - dense_map ctor{}; - dense_map base{}; - dense_map conv{}; - dense_map data{}; - dense_map func{}; meta_dtor_node dtor{}; + std::shared_ptr details{}; }; template diff --git a/test/entt/meta/meta_base.cpp b/test/entt/meta/meta_base.cpp index e2e67a608..a070077d4 100644 --- a/test/entt/meta/meta_base.cpp +++ b/test/entt/meta/meta_base.cpp @@ -183,6 +183,7 @@ TEST_F(MetaBase, ReRegistration) { auto *node = entt::internal::resolve(); - ASSERT_FALSE(node->base.empty()); - ASSERT_EQ(node->base.size(), 2u); + ASSERT_TRUE(node->details); + ASSERT_FALSE(node->details->base.empty()); + ASSERT_EQ(node->details->base.size(), 2u); } diff --git a/test/entt/meta/meta_conv.cpp b/test/entt/meta/meta_conv.cpp index 0671cfa33..8ae223a77 100644 --- a/test/entt/meta/meta_conv.cpp +++ b/test/entt/meta/meta_conv.cpp @@ -64,6 +64,7 @@ TEST_F(MetaConv, ReRegistration) { auto *node = entt::internal::resolve(); - ASSERT_FALSE(node->conv.empty()); - ASSERT_EQ(node->conv.size(), 3u); + ASSERT_TRUE(node->details); + ASSERT_FALSE(node->details->conv.empty()); + ASSERT_EQ(node->details->conv.size(), 3u); } diff --git a/test/entt/meta/meta_ctor.cpp b/test/entt/meta/meta_ctor.cpp index 0b3fb3d63..fb01acbea 100644 --- a/test/entt/meta/meta_ctor.cpp +++ b/test/entt/meta/meta_ctor.cpp @@ -207,7 +207,8 @@ TEST_F(MetaCtor, ReRegistration) { auto *node = entt::internal::resolve(); - ASSERT_FALSE(node->ctor.empty()); + ASSERT_TRUE(node->details); + ASSERT_FALSE(node->details->ctor.empty()); // implicitly generated default constructor is not cleared ASSERT_NE(node->default_constructor, nullptr); } diff --git a/test/entt/meta/meta_data.cpp b/test/entt/meta/meta_data.cpp index a3d7f41d3..349c5606f 100644 --- a/test/entt/meta/meta_data.cpp +++ b/test/entt/meta/meta_data.cpp @@ -646,13 +646,15 @@ TEST_F(MetaData, ReRegistration) { auto *node = entt::internal::resolve(); auto type = entt::resolve(); - ASSERT_FALSE(node->data.empty()); - ASSERT_EQ(node->data.size(), 1u); + ASSERT_TRUE(node->details); + ASSERT_FALSE(node->details->data.empty()); + ASSERT_EQ(node->details->data.size(), 1u); ASSERT_TRUE(type.data("value"_hs)); entt::meta().data<&base_t::value>("field"_hs); - ASSERT_EQ(node->data.size(), 2u); + ASSERT_TRUE(node->details); + ASSERT_EQ(node->details->data.size(), 2u); ASSERT_TRUE(type.data("value"_hs)); ASSERT_TRUE(type.data("field"_hs)); } diff --git a/test/entt/meta/meta_prop.cpp b/test/entt/meta/meta_prop.cpp index 2c3e1ddca..83da05389 100644 --- a/test/entt/meta/meta_prop.cpp +++ b/test/entt/meta/meta_prop.cpp @@ -89,8 +89,9 @@ TEST_F(MetaProp, ReRegistration) { auto *node = entt::internal::resolve(); auto type = entt::resolve(); - ASSERT_FALSE(node->prop.empty()); - ASSERT_EQ(node->prop.size(), 1u); + ASSERT_TRUE(node->details); + ASSERT_FALSE(node->details->prop.empty()); + ASSERT_EQ(node->details->prop.size(), 1u); ASSERT_TRUE(type.prop("int"_hs)); ASSERT_EQ(type.prop("int"_hs).value().cast(), 42); @@ -98,8 +99,9 @@ TEST_F(MetaProp, ReRegistration) { entt::meta().prop("int"_hs, 0); entt::meta().prop("double"_hs, 3.); - ASSERT_FALSE(node->prop.empty()); - ASSERT_EQ(node->prop.size(), 2u); + ASSERT_TRUE(node->details); + ASSERT_FALSE(node->details->prop.empty()); + ASSERT_EQ(node->details->prop.size(), 2u); ASSERT_TRUE(type.prop("int"_hs)); ASSERT_TRUE(type.prop("double"_hs));