Browse Source

meta:
* a more robust meta_any::allow_cast (reviewed all overloads)
* removed internal meta_info dispatcher to reduce useless instantiations
* reviewed meta_conversion_helper

Michele Caini 4 years ago
parent
commit
2f22395eea

+ 14 - 14
src/entt/meta/factory.hpp

@@ -142,7 +142,7 @@ template<typename Type>
 struct meta_factory<Type> {
     /*! @brief Default constructor. */
     meta_factory()
-        : owner{internal::meta_info<Type>::resolve()}
+        : owner{internal::meta_node<Type>::resolve()}
     {}
 
     /**
@@ -177,7 +177,7 @@ struct meta_factory<Type> {
 
         static internal::meta_base_node node{
             nullptr,
-            internal::meta_info<Base>::resolve(),
+            internal::meta_node<Base>::resolve(),
             [](const void *instance) ENTT_NOEXCEPT -> const void * {
                 return static_cast<const Base *>(static_cast<const Type *>(instance));
             }
@@ -205,11 +205,11 @@ struct meta_factory<Type> {
      */
     template<auto Candidate>
     std::enable_if_t<std::is_member_function_pointer_v<decltype(Candidate)>, meta_factory<Type>> conv() ENTT_NOEXCEPT {
-        using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
+        using conv_type = std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
 
         static internal::meta_conv_node node{
             nullptr,
-            internal::meta_info<conv_type>::resolve(),
+            internal::meta_node<conv_type>::resolve(),
             [](const void *instance) -> meta_any {
                 return forward_as_meta(static_cast<const Type *>(instance)->*Candidate)();
             }
@@ -226,11 +226,11 @@ struct meta_factory<Type> {
     /*! @copydoc conv */
     template<auto Candidate>
     std::enable_if_t<!std::is_member_function_pointer_v<decltype(Candidate)>, meta_factory<Type>> conv() ENTT_NOEXCEPT {
-        using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
+        using conv_type = std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
 
         static internal::meta_conv_node node{
             nullptr,
-            internal::meta_info<conv_type>::resolve(),
+            internal::meta_node<conv_type>::resolve(),
             [](const void *instance) -> meta_any {
                 return forward_as_meta(Candidate(*static_cast<const Type *>(instance)));
             }
@@ -259,7 +259,7 @@ struct meta_factory<Type> {
 
         static internal::meta_conv_node node{
             nullptr,
-            internal::meta_info<To>::resolve(),
+            internal::meta_node<std::remove_const_t<std::remove_reference_t<To>>>::resolve(),
             [](const void *instance) -> meta_any {
                 return forward_as_meta(static_cast<To>(*static_cast<const Type *>(instance)));
             }
@@ -391,7 +391,7 @@ struct meta_factory<Type> {
                 internal::meta_traits::IS_NONE
                     | ((std::is_same_v<Type, data_type> || std::is_const_v<data_type>) ? internal::meta_traits::IS_CONST : internal::meta_traits::IS_NONE)
                     | internal::meta_traits::IS_STATIC,
-                internal::meta_info<data_type>::resolve(),
+                internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
                 &meta_setter<Type, Data>,
                 &meta_getter<Type, Data, Policy>
             };
@@ -431,16 +431,16 @@ struct meta_factory<Type> {
      */
     template<auto Setter, auto Getter, typename Policy = as_is_t>
     auto data(const id_type id) ENTT_NOEXCEPT {
-        using underlying_type = std::remove_reference_t<std::invoke_result_t<decltype(Getter), Type &>>;
+        using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Getter), Type &>>;
 
         static internal::meta_data_node node{
             {},
             nullptr,
             nullptr,
             internal::meta_traits::IS_NONE
-                | ((std::is_same_v<decltype(Setter), std::nullptr_t> || (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<underlying_type>)) ? internal::meta_traits::IS_CONST : internal::meta_traits::IS_NONE)
+                | ((std::is_same_v<decltype(Setter), std::nullptr_t> || (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<data_type>)) ? internal::meta_traits::IS_CONST : internal::meta_traits::IS_NONE)
                 /* this is never static */,
-            internal::meta_info<underlying_type>::resolve(),
+            internal::meta_node<std::remove_const_t<std::remove_reference_t<data_type>>>::resolve(),
             &meta_setter<Type, Setter>,
             &meta_getter<Type, Getter, Policy>
         };
@@ -482,7 +482,7 @@ struct meta_factory<Type> {
             internal::meta_traits::IS_NONE
                 | (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),
-            internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename descriptor::return_type>>::resolve(),
+            internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>>::resolve(),
             &meta_arg<typename descriptor::args_type>,
             &meta_invoke<Type, Candidate, Policy>
         };
@@ -523,7 +523,7 @@ private:
  */
 template<typename Type>
 [[nodiscard]] auto meta() ENTT_NOEXCEPT {
-    auto * const node = internal::meta_info<Type>::resolve();
+    auto * const node = internal::meta_node<Type>::resolve();
     // extended meta factory to allow assigning properties to opaque meta types
     return meta_factory<Type, Type>{&node->prop};
 }
@@ -577,7 +577,7 @@ inline void meta_reset(const id_type id) ENTT_NOEXCEPT {
  */
 template<typename Type>
 void meta_reset() ENTT_NOEXCEPT {
-    meta_reset(internal::meta_info<Type>::resolve()->id);
+    meta_reset(internal::meta_node<Type>::resolve()->id);
 }
 
 

+ 41 - 33
src/entt/meta/meta.hpp

@@ -47,7 +47,7 @@ public:
      */
     template<typename Type>
     meta_sequence_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT
-        : value_type_node{internal::meta_info<typename Type::value_type>::resolve()},
+        : value_type_node{internal::meta_node<std::remove_const_t<std::remove_reference_t<typename Type::value_type>>>::resolve()},
           size_fn{&meta_sequence_container_traits<Type>::size},
           resize_fn{&meta_sequence_container_traits<Type>::resize},
           clear_fn{&meta_sequence_container_traits<Type>::clear},
@@ -105,9 +105,9 @@ public:
     template<typename Type>
     meta_associative_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT
         : key_only_container{meta_associative_container_traits<Type>::key_only()},
-          key_type_node{internal::meta_info<typename Type::key_type>::resolve()},
+          key_type_node{internal::meta_node<std::remove_const_t<std::remove_reference_t<typename Type::key_type>>>::resolve()},
           mapped_type_node{nullptr},
-          value_type_node{internal::meta_info<typename Type::value_type>::resolve()},
+          value_type_node{internal::meta_node<std::remove_const_t<std::remove_reference_t<typename Type::value_type>>>::resolve()},
           size_fn{&meta_associative_container_traits<Type>::size},
           clear_fn{&meta_associative_container_traits<Type>::clear},
           begin_fn{&meta_associative_container_traits<Type>::begin},
@@ -118,7 +118,7 @@ public:
           storage{std::move(instance)}
     {
         if constexpr(!meta_associative_container_traits<Type>::key_only()) {
-            mapped_type_node = internal::meta_info<typename Type::mapped_type>::resolve();
+            mapped_type_node = internal::meta_node<std::remove_const_t<std::remove_reference_t<typename Type::mapped_type>>>::resolve();
         }
     }
 
@@ -218,7 +218,7 @@ public:
     template<typename Type, typename... Args>
     explicit meta_any(std::in_place_type_t<Type>, Args &&... args)
         : storage{std::in_place_type<Type>, std::forward<Args>(args)...},
-          node{internal::meta_info<Type>::resolve()},
+          node{internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve()},
           vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>}
     {}
 
@@ -230,8 +230,8 @@ public:
     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
     meta_any(Type &&value)
         : storage{std::forward<Type>(value)},
-          node{internal::meta_info<std::decay_t<Type>>::resolve()},
-          vtable{&basic_vtable<std::decay_t<Type>>}
+          node{internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve()},
+          vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>}
     {}
 
     /**
@@ -361,9 +361,7 @@ public:
      */
     template<typename Type>
     [[nodiscard]] const Type * try_cast() const {
-        if(!node) { return nullptr; }
-
-        if(const auto info = type_id<Type>(); node->info == info) {
+        if(const auto info = type_id<Type>(); node && node->info == info) {
             return any_cast<Type>(&storage);
         } else if(const auto *base = internal::visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type->info == info; }, node); base) {
             return static_cast<const Type *>(base->cast(storage.data()));
@@ -375,19 +373,13 @@ public:
     /*! @copydoc try_cast */
     template<typename Type>
     [[nodiscard]] Type * try_cast() {
-        if constexpr(std::is_const_v<Type>) {
-            return std::as_const(*this).try_cast<Type>();
-        } else {
-            if(!node) { return nullptr; }
-
-            if(const auto info = type_id<Type>(); node->info == info) {
-                return any_cast<Type>(&storage);
-            } else if(const auto *base = internal::visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type->info == info; }, node); base) {
-                return const_cast<Type *>(static_cast<const Type *>(base->cast(storage.data())));
-            }
-
-            return nullptr;
+        if(const auto info = type_id<Type>(); node && node->info == info) {
+            return any_cast<Type>(&storage);
+        } else if(const auto *base = internal::visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type->info == info; }, node); base) {
+            return static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(base->cast(static_cast<constness_as_t<any, Type> &>(storage).data())));
         }
+        
+        return nullptr;
     }
 
     /**
@@ -440,7 +432,13 @@ public:
      */
     template<typename Type>
     [[nodiscard]] meta_any allow_cast() const {
-        return allow_cast(internal::meta_info<Type>::resolve());
+        const auto other = allow_cast(internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve());
+
+        if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) {
+            return other.storage.owner() ? other : meta_any{};
+        } else {
+            return other;
+        }
     }
 
     /**
@@ -450,7 +448,20 @@ public:
      */
     template<typename Type>
     bool allow_cast() {
-        return allow_cast(internal::meta_info<Type>::resolve());
+        if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) {
+            if(auto other = std::as_const(*this).allow_cast(internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve()); other) {
+                if(other.storage.owner()) {
+                    std::swap(*this, other);
+                    return true;
+                }
+
+                return (storage.data() != nullptr);
+            }
+
+            return false;
+        } else {
+            return allow_cast(internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve());
+        }
     }
 
     /**
@@ -464,7 +475,7 @@ public:
         release();
         vtable = &basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>;
         storage.emplace<Type>(std::forward<Args>(args)...);
-        node = internal::meta_info<Type>::resolve();
+        node = internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve();
     }
 
     /*! @brief Destroys contained object */
@@ -1045,7 +1056,7 @@ class meta_type {
     template<typename... Args, auto... Index>
     [[nodiscard]] static const internal::meta_ctor_node * ctor(const internal::meta_ctor_node *curr, std::index_sequence<Index...>) {
         for(; curr; curr = curr->next) {
-            if(curr->arity == sizeof...(Args) && (can_cast_or_convert(internal::meta_info<Args>::resolve(), curr->arg(Index)) && ...)) {
+            if(curr->arity == sizeof...(Args) && (can_cast_or_convert(internal::meta_node<std::remove_const_t<std::remove_reference_t<Args>>>::resolve(), curr->arg(Index)) && ...)) {
                 return curr;
             }
         }
@@ -1501,18 +1512,15 @@ bool meta_any::set(const id_type id, Type &&value) {
 
 
 [[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const {
-    if(!node) { return {}; }
-
-    if(const auto info = type.info(); node->info == info || internal::visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type->info == info; }, node)) {
+    if(const auto info = type.info(); (node && node->info == info) || internal::visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type->info == info; }, node)) {
         return as_ref();
     } else if(const auto * const conv = internal::visit<&internal::meta_type_node::conv>([info](const auto *curr) { return curr->type->info == info; }, node); conv) {
         return conv->conv(storage.data());
-    } else if((type.is_arithmetic() || type.is_enum()) && node->conversion_helper) {
+    } else if(node && node->conversion_helper && (type.is_arithmetic() || type.is_enum())) {
         // exploits the fact that arithmetic types and enums are also default constructible
         auto other = type.construct();
-        const double value = node->conversion_helper(storage, nullptr);
         ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found");
-        other.node->conversion_helper(other.storage, &value);
+        other.node->conversion_helper(other.storage, node->conversion_helper(storage, {}));
         return other;
     }
 
@@ -1523,7 +1531,7 @@ bool meta_any::set(const id_type id, Type &&value) {
 inline bool meta_any::allow_cast(const meta_type &type) {
     if(auto other = std::as_const(*this).allow_cast(type); other) {
         if(other.storage.owner()) {
-            *this = std::move(other);
+            std::swap(*this, other);
         }
 
         return true;

+ 6 - 10
src/entt/meta/node.hpp

@@ -122,7 +122,7 @@ struct meta_type_node {
     const size_type size_of;
     const meta_traits traits;
     meta_any(* const default_constructor)();
-    double(* const conversion_helper)(const any &, const double *);
+    double(* const conversion_helper)(const any &, const any &);
     const meta_template_node *const templ;
     meta_ctor_node *ctor{nullptr};
     meta_base_node *base{nullptr};
@@ -151,12 +151,12 @@ class ENTT_API meta_node {
 
     [[nodiscard]] static decltype(meta_type_node::conversion_helper) meta_conversion_helper() ENTT_NOEXCEPT {
         if constexpr(std::is_arithmetic_v<Type>) {
-            return +[](const any &storage, const double *value) {
-                return value ? static_cast<double>(any_cast<Type &>(const_cast<any &>(storage)) = static_cast<Type>(*value)) : static_cast<double>(any_cast<const Type &>(storage));
+            return +[](const any &storage, const any &value) {
+                return value ? static_cast<double>(any_cast<Type &>(const_cast<any &>(storage)) = static_cast<Type>(any_cast<double>(value))) : static_cast<double>(any_cast<const Type &>(storage));
             };
         } else if constexpr(std::is_enum_v<Type>) {
-            return +[](const any &storage, const double *value) {
-                return value ? static_cast<double>(any_cast<Type &>(const_cast<any &>(storage)) = Type{static_cast<std::underlying_type_t<Type>>(*value)}) : static_cast<double>(any_cast<const Type &>(storage));
+            return +[](const any &storage, const any &value) {
+                return value ? static_cast<double>(any_cast<Type &>(const_cast<any &>(storage)) = Type{static_cast<std::underlying_type_t<Type>>(any_cast<double>(value))}) : static_cast<double>(any_cast<const Type &>(storage));
             };
         } else {
             return nullptr;
@@ -206,13 +206,9 @@ public:
 };
 
 
-template<typename Type>
-struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>> {};
-
-
 template<typename... Args>
 [[nodiscard]] meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
-    meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_info<Args>::resolve()...};
+    meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node<std::remove_const_t<std::remove_reference_t<Args>>>::resolve()...};
     return args[index + 1u];
 }
 

+ 1 - 1
src/entt/meta/resolve.hpp

@@ -20,7 +20,7 @@ namespace entt {
  */
 template<typename Type>
 [[nodiscard]] meta_type resolve() ENTT_NOEXCEPT {
-    return internal::meta_info<Type>::resolve();
+    return internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve();
 }
 
 

+ 59 - 0
test/entt/meta/meta_any.cpp

@@ -804,6 +804,7 @@ TEST_F(MetaAny, TryCast) {
     ASSERT_EQ(any.try_cast<fat_t>(), any.data());
     ASSERT_EQ(std::as_const(any).try_cast<empty_t>(), any.try_cast<empty_t>());
     ASSERT_EQ(std::as_const(any).try_cast<fat_t>(), any.data());
+    ASSERT_EQ(std::as_const(any).try_cast<int>(), nullptr);
 }
 
 TEST_F(MetaAny, Cast) {
@@ -823,6 +824,64 @@ TEST_F(MetaAny, Cast) {
     ASSERT_EQ(any.cast<fat_t>().value[0u], 3.);
 }
 
+TEST_F(MetaAny, AllowCast) {
+    entt::meta_any clazz{clazz_t{}};
+    entt::meta_any fat{fat_t{}};
+    entt::meta_any arithmetic{42};
+    auto as_cref = entt::forward_as_meta(arithmetic.cast<const int &>());
+
+    ASSERT_TRUE(clazz);
+    ASSERT_TRUE(fat);
+    ASSERT_TRUE(arithmetic);
+    ASSERT_TRUE(as_cref);
+
+    ASSERT_TRUE(clazz.allow_cast<clazz_t>());
+    ASSERT_TRUE(clazz.allow_cast<clazz_t &>());
+    ASSERT_TRUE(clazz.allow_cast<const clazz_t &>());
+    ASSERT_EQ(clazz.type(), entt::resolve<clazz_t>());
+
+    ASSERT_TRUE(clazz.allow_cast<const int &>());
+    ASSERT_EQ(clazz.type(), entt::resolve<int>());
+    ASSERT_TRUE(clazz.allow_cast<int>());
+    ASSERT_TRUE(clazz.allow_cast<int &>());
+    ASSERT_TRUE(clazz.allow_cast<const int &>());
+
+    ASSERT_TRUE(fat.allow_cast<fat_t>());
+    ASSERT_TRUE(fat.allow_cast<fat_t &>());
+    ASSERT_TRUE(fat.allow_cast<const empty_t &>());
+    ASSERT_EQ(fat.type(), entt::resolve<fat_t>());
+    ASSERT_FALSE(fat.allow_cast<int>());
+
+    ASSERT_TRUE(std::as_const(fat).allow_cast<fat_t>());
+    ASSERT_FALSE(std::as_const(fat).allow_cast<fat_t &>());
+    ASSERT_TRUE(std::as_const(fat).allow_cast<const empty_t &>());
+    ASSERT_EQ(fat.type(), entt::resolve<fat_t>());
+    ASSERT_FALSE(fat.allow_cast<int>());
+
+    ASSERT_TRUE(arithmetic.allow_cast<int>());
+    ASSERT_TRUE(arithmetic.allow_cast<int &>());
+    ASSERT_TRUE(arithmetic.allow_cast<const int &>());
+    ASSERT_EQ(arithmetic.type(), entt::resolve<int>());
+    ASSERT_FALSE(arithmetic.allow_cast<fat_t>());
+
+    ASSERT_TRUE(arithmetic.allow_cast<double &>());
+    ASSERT_EQ(arithmetic.type(), entt::resolve<double>());
+    ASSERT_EQ(arithmetic.cast<double &>(), 42.);
+
+    ASSERT_TRUE(arithmetic.allow_cast<const float &>());
+    ASSERT_EQ(arithmetic.type(), entt::resolve<float>());
+    ASSERT_EQ(arithmetic.cast<float &>(), 42.f);
+
+    ASSERT_TRUE(as_cref.allow_cast<int>());
+    ASSERT_FALSE(as_cref.allow_cast<int &>());
+    ASSERT_TRUE(as_cref.allow_cast<const int &>());
+    ASSERT_EQ(as_cref.type(), entt::resolve<int>());
+    ASSERT_FALSE(as_cref.allow_cast<fat_t>());
+
+    ASSERT_TRUE(as_cref.allow_cast<double &>());
+    ASSERT_EQ(as_cref.type(), entt::resolve<double>());
+}
+
 TEST_F(MetaAny, Convert) {
     entt::meta_any any{clazz_t{}};
     any.cast<clazz_t &>().value = 42;

+ 1 - 1
test/entt/meta/meta_base.cpp

@@ -48,7 +48,7 @@ TEST_F(MetaBase, Functionalities) {
 TEST_F(MetaBase, ReRegistration) {
     SetUp();
 
-    auto *node = entt::internal::meta_info<derived_t>::resolve();
+    auto *node = entt::internal::meta_node<derived_t>::resolve();
 
     ASSERT_NE(node->base, nullptr);
     ASSERT_EQ(node->base->next, nullptr);

+ 1 - 1
test/entt/meta/meta_conv.cpp

@@ -49,7 +49,7 @@ TEST_F(MetaConv, Functionalities) {
 TEST_F(MetaConv, ReRegistration) {
     SetUp();
 
-    auto *node = entt::internal::meta_info<clazz_t>::resolve();
+    auto *node = entt::internal::meta_node<clazz_t>::resolve();
 
     ASSERT_NE(node->conv, nullptr);
     ASSERT_NE(node->conv->next, nullptr);

+ 1 - 1
test/entt/meta/meta_ctor.cpp

@@ -281,7 +281,7 @@ TEST_F(MetaCtor, NonDefaultConstructibleType) {
 TEST_F(MetaCtor, ReRegistration) {
     SetUp();
 
-    auto *node = entt::internal::meta_info<double>::resolve();
+    auto *node = entt::internal::meta_node<double>::resolve();
 
     ASSERT_NE(node->ctor, nullptr);
     // implicitly generated default constructor is not cleared

+ 1 - 1
test/entt/meta/meta_data.cpp

@@ -543,7 +543,7 @@ TEST_F(MetaData, ReRegistration) {
 
     SetUp();
 
-    auto *node = entt::internal::meta_info<base_t>::resolve();
+    auto *node = entt::internal::meta_node<base_t>::resolve();
     auto type = entt::resolve<base_t>();
 
     ASSERT_NE(node->data, nullptr);

+ 1 - 1
test/entt/meta/meta_dtor.cpp

@@ -100,7 +100,7 @@ TEST_F(MetaDtor, AsRefConstruction) {
 TEST_F(MetaDtor, ReRegistration) {
     SetUp();
 
-    auto *node = entt::internal::meta_info<clazz_t>::resolve();
+    auto *node = entt::internal::meta_node<clazz_t>::resolve();
 
     ASSERT_NE(node->dtor, nullptr);
 

+ 1 - 1
test/entt/meta/meta_prop.cpp

@@ -72,7 +72,7 @@ TEST_F(MetaProp, ReRegistration) {
 
     SetUp();
 
-    auto *node = entt::internal::meta_info<base_1_t>::resolve();
+    auto *node = entt::internal::meta_node<base_1_t>::resolve();
     auto type = entt::resolve<base_1_t>();
 
     ASSERT_NE(node->prop, nullptr);