Browse Source

meta: meta_type is void (and safe again) when empty

skypjack 5 tháng trước cách đây
mục cha
commit
cb9d8ac3f0
2 tập tin đã thay đổi với 94 bổ sung39 xóa
  1. 38 39
      src/entt/meta/meta.hpp
  2. 56 0
      test/entt/meta/meta_type.cpp

+ 38 - 39
src/entt/meta/meta.hpp

@@ -1084,9 +1084,8 @@ private:
 
 /*! @brief Opaque wrapper for types. */
 class meta_type {
-    [[nodiscard]] auto &node_or_assert() const noexcept {
-        ENTT_ASSERT(node != nullptr, "Invalid pointer to node");
-        return *node;
+    [[nodiscard]] const auto &fetch_node() const {
+        return (node == nullptr) ? *(node = &internal::resolve<void>(internal::meta_context::from(*ctx))) : *node;
     }
 
     template<typename Func>
@@ -1113,7 +1112,7 @@ class meta_type {
 
                     if(const auto &info = other.info(); info == type.info()) {
                         ++match;
-                    } else if(!(type.node_or_assert().conversion_helper && other.node_or_assert().conversion_helper) && !(type.node_or_assert().details && (internal::find_member<&internal::meta_base_node::type>(type.node_or_assert().details->base, info.hash()) || internal::find_member<&internal::meta_conv_node::type>(type.node_or_assert().details->conv, info.hash())))) {
+                    } else if(!(type.fetch_node().conversion_helper && other.fetch_node().conversion_helper) && !(type.fetch_node().details && (internal::find_member<&internal::meta_base_node::type>(type.fetch_node().details->base, info.hash()) || internal::find_member<&internal::meta_conv_node::type>(type.fetch_node().details->conv, info.hash())))) {
                         break;
                     }
                 }
@@ -1171,7 +1170,7 @@ public:
      * @return The type info object of the underlying type.
      */
     [[nodiscard]] const type_info &info() const noexcept {
-        return *node_or_assert().info;
+        return *fetch_node().info;
     }
 
     /**
@@ -1179,7 +1178,7 @@ public:
      * @return The identifier assigned to the type.
      */
     [[nodiscard]] id_type id() const noexcept {
-        return node_or_assert().id;
+        return fetch_node().id;
     }
 
     /**
@@ -1187,7 +1186,7 @@ public:
      * @return The name assigned to the type, if any.
      */
     [[nodiscard]] const char *name() const noexcept {
-        return node_or_assert().name;
+        return fetch_node().name;
     }
 
     /**
@@ -1195,7 +1194,7 @@ public:
      * @return The size of the underlying type if known, 0 otherwise.
      */
     [[nodiscard]] size_type size_of() const noexcept {
-        return node_or_assert().size_of;
+        return fetch_node().size_of;
     }
 
     /**
@@ -1204,7 +1203,7 @@ public:
      * otherwise.
      */
     [[nodiscard]] bool is_arithmetic() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_arithmetic);
+        return !!(fetch_node().traits & internal::meta_traits::is_arithmetic);
     }
 
     /**
@@ -1212,7 +1211,7 @@ public:
      * @return True if the underlying type is an integral type, false otherwise.
      */
     [[nodiscard]] bool is_integral() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_integral);
+        return !!(fetch_node().traits & internal::meta_traits::is_integral);
     }
 
     /**
@@ -1220,7 +1219,7 @@ public:
      * @return True if the underlying type is a signed type, false otherwise.
      */
     [[nodiscard]] bool is_signed() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_signed);
+        return !!(fetch_node().traits & internal::meta_traits::is_signed);
     }
 
     /**
@@ -1228,7 +1227,7 @@ public:
      * @return True if the underlying type is an array type, false otherwise.
      */
     [[nodiscard]] bool is_array() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_array);
+        return !!(fetch_node().traits & internal::meta_traits::is_array);
     }
 
     /**
@@ -1236,7 +1235,7 @@ public:
      * @return True if the underlying type is an enum, false otherwise.
      */
     [[nodiscard]] bool is_enum() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_enum);
+        return !!(fetch_node().traits & internal::meta_traits::is_enum);
     }
 
     /**
@@ -1244,7 +1243,7 @@ public:
      * @return True if the underlying type is a class, false otherwise.
      */
     [[nodiscard]] bool is_class() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_class);
+        return !!(fetch_node().traits & internal::meta_traits::is_class);
     }
 
     /**
@@ -1252,7 +1251,7 @@ public:
      * @return True if the underlying type is a pointer, false otherwise.
      */
     [[nodiscard]] bool is_pointer() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_pointer);
+        return !!(fetch_node().traits & internal::meta_traits::is_pointer);
     }
 
     /**
@@ -1261,7 +1260,7 @@ public:
      * doesn't refer to a pointer type.
      */
     [[nodiscard]] meta_type remove_pointer() const noexcept {
-        return meta_type{*ctx, node_or_assert().remove_pointer(internal::meta_context::from(*ctx))};
+        return meta_type{*ctx, fetch_node().remove_pointer(internal::meta_context::from(*ctx))};
     }
 
     /**
@@ -1269,7 +1268,7 @@ public:
      * @return True if the underlying type is pointer-like, false otherwise.
      */
     [[nodiscard]] bool is_pointer_like() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_pointer_like);
+        return !!(fetch_node().traits & internal::meta_traits::is_pointer_like);
     }
 
     /**
@@ -1277,7 +1276,7 @@ public:
      * @return True if the type is a sequence container, false otherwise.
      */
     [[nodiscard]] bool is_sequence_container() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_sequence_container);
+        return !!(fetch_node().traits & internal::meta_traits::is_sequence_container);
     }
 
     /**
@@ -1285,7 +1284,7 @@ public:
      * @return True if the type is an associative container, false otherwise.
      */
     [[nodiscard]] bool is_associative_container() const noexcept {
-        return !!(node_or_assert().traits & internal::meta_traits::is_associative_container);
+        return !!(fetch_node().traits & internal::meta_traits::is_associative_container);
     }
 
     /**
@@ -1295,7 +1294,7 @@ public:
      * false otherwise.
      */
     [[nodiscard]] bool is_template_specialization() const noexcept {
-        return (node_or_assert().templ.arity != 0u);
+        return (fetch_node().templ.arity != 0u);
     }
 
     /**
@@ -1303,7 +1302,7 @@ public:
      * @return The number of template arguments.
      */
     [[nodiscard]] size_type template_arity() const noexcept {
-        return node_or_assert().templ.arity;
+        return fetch_node().templ.arity;
     }
 
     /**
@@ -1311,7 +1310,7 @@ public:
      * @return The tag for the class template of the underlying type.
      */
     [[nodiscard]] meta_type template_type() const noexcept {
-        return (node_or_assert().templ.resolve != nullptr) ? meta_type{*ctx, node_or_assert().templ.resolve(internal::meta_context::from(*ctx))} : meta_type{};
+        return (fetch_node().templ.resolve != nullptr) ? meta_type{*ctx, fetch_node().templ.resolve(internal::meta_context::from(*ctx))} : meta_type{};
     }
 
     /**
@@ -1320,7 +1319,7 @@ public:
      * @return The type of the i-th template argument of a type.
      */
     [[nodiscard]] meta_type template_arg(const size_type index) const noexcept {
-        return index < template_arity() ? meta_type{*ctx, node_or_assert().templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{};
+        return index < template_arity() ? meta_type{*ctx, fetch_node().templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{};
     }
 
     /**
@@ -1330,7 +1329,7 @@ public:
      */
     [[nodiscard]] bool can_cast(const meta_type &other) const noexcept {
         // casting this is UB in all cases but we aren't going to use the resulting pointer, so...
-        return other && ((*this == other) || (internal::try_cast(internal::meta_context::from(*ctx), node_or_assert(), other.node_or_assert().info->hash(), this) != nullptr));
+        return other && ((*this == other) || (internal::try_cast(internal::meta_context::from(*ctx), fetch_node(), other.fetch_node().info->hash(), this) != nullptr));
     }
 
     /**
@@ -1339,7 +1338,7 @@ public:
      * @return True if the conversion is allowed, false otherwise.
      */
     [[nodiscard]] bool can_convert(const meta_type &other) const noexcept {
-        return (internal::try_convert(internal::meta_context::from(*ctx), node_or_assert(), other.info().hash(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast<void>(args), 1) + ... + 0u); }) != 0u);
+        return (internal::try_convert(internal::meta_context::from(*ctx), fetch_node(), other.info().hash(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast<void>(args), 1) + ... + 0u); }) != 0u);
     }
 
     /**
@@ -1348,7 +1347,7 @@ public:
      */
     [[nodiscard]] meta_range<meta_type, typename decltype(internal::meta_type_descriptor::base)::const_iterator> base() const noexcept {
         using range_type = meta_range<meta_type, typename decltype(internal::meta_type_descriptor::base)::const_iterator>;
-        return node_or_assert().details ? range_type{{*ctx, node_or_assert().details->base.cbegin()}, {*ctx, node_or_assert().details->base.cend()}} : range_type{};
+        return fetch_node().details ? range_type{{*ctx, fetch_node().details->base.cbegin()}, {*ctx, fetch_node().details->base.cend()}} : range_type{};
     }
 
     /**
@@ -1357,7 +1356,7 @@ public:
      */
     [[nodiscard]] meta_range<meta_data, typename decltype(internal::meta_type_descriptor::data)::const_iterator> data() const noexcept {
         using range_type = meta_range<meta_data, typename decltype(internal::meta_type_descriptor::data)::const_iterator>;
-        return node_or_assert().details ? range_type{{*ctx, node_or_assert().details->data.cbegin()}, {*ctx, node_or_assert().details->data.cend()}} : range_type{};
+        return fetch_node().details ? range_type{{*ctx, fetch_node().details->data.cbegin()}, {*ctx, fetch_node().details->data.cend()}} : range_type{};
     }
 
     /**
@@ -1366,7 +1365,7 @@ public:
      * @return The registered meta data for the given identifier, if any.
      */
     [[nodiscard]] meta_data data(const id_type id) const {
-        const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node_or_assert(), id);
+        const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), fetch_node(), id);
         return (elem != nullptr) ? meta_data{*ctx, *elem} : meta_data{};
     }
 
@@ -1376,7 +1375,7 @@ public:
      */
     [[nodiscard]] meta_range<meta_func, typename decltype(internal::meta_type_descriptor::func)::const_iterator> func() const noexcept {
         using return_type = meta_range<meta_func, typename decltype(internal::meta_type_descriptor::func)::const_iterator>;
-        return node_or_assert().details ? return_type{{*ctx, node_or_assert().details->func.cbegin()}, {*ctx, node_or_assert().details->func.cend()}} : return_type{};
+        return fetch_node().details ? return_type{{*ctx, fetch_node().details->func.cbegin()}, {*ctx, fetch_node().details->func.cend()}} : return_type{};
     }
 
     /**
@@ -1385,7 +1384,7 @@ public:
      * @return The registered meta function for the given identifier, if any.
      */
     [[nodiscard]] meta_func func(const id_type id) const {
-        const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node_or_assert(), id);
+        const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), fetch_node(), id);
         return (elem != nullptr) ? meta_func{*ctx, *elem} : meta_func{};
     }
 
@@ -1396,13 +1395,13 @@ public:
      * @return A wrapper containing the new instance, if any.
      */
     [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const {
-        if(const auto &ref = node_or_assert(); ref.details) {
+        if(const auto &ref = fetch_node(); ref.details) {
             if(const auto *candidate = lookup(args, sz, false, [first = ref.details->ctor.cbegin(), last = ref.details->ctor.cend()]() mutable { return first == last ? nullptr : &*(first++); }); candidate) {
                 return candidate->invoke(*ctx, args);
             }
         }
 
-        if(const auto &ref = node_or_assert(); (sz == 0u) && (ref.default_constructor != nullptr)) {
+        if(const auto &ref = fetch_node(); (sz == 0u) && (ref.default_constructor != nullptr)) {
             return ref.default_constructor(*ctx);
         }
 
@@ -1428,7 +1427,7 @@ public:
      * @return A wrapper that references the given instance.
      */
     [[nodiscard]] meta_any from_void(void *elem, bool transfer_ownership = false) const {
-        return ((elem != nullptr) && (node_or_assert().from_void != nullptr)) ? node_or_assert().from_void(*ctx, elem, transfer_ownership ? elem : nullptr) : meta_any{meta_ctx_arg, *ctx};
+        return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, elem, transfer_ownership ? elem : nullptr) : meta_any{meta_ctx_arg, *ctx};
     }
 
     /**
@@ -1437,7 +1436,7 @@ public:
      * @return A wrapper that references the given instance.
      */
     [[nodiscard]] meta_any from_void(const void *elem) const {
-        return ((elem != nullptr) && (node_or_assert().from_void != nullptr)) ? node_or_assert().from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx};
+        return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx};
     }
 
     /**
@@ -1450,7 +1449,7 @@ public:
      */
     // NOLINTNEXTLINE(modernize-use-nodiscard)
     meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const {
-        if(const auto &ref = node_or_assert(); ref.details) {
+        if(const auto &ref = fetch_node(); ref.details) {
             if(auto *elem = internal::find_member<&internal::meta_func_node::id>(ref.details->func, id); elem != nullptr) {
                 if(const auto *candidate = lookup(args, sz, (instance->base().policy() == any_policy::cref), [curr = elem]() mutable { return (curr != nullptr) ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
                     return candidate->invoke(meta_handle{*ctx, std::move(instance)}, args);
@@ -1510,12 +1509,12 @@ public:
     /*! @copydoc meta_data::traits */
     template<typename Type>
     [[nodiscard]] Type traits() const noexcept {
-        return internal::meta_to_user_traits<Type>(node_or_assert().traits);
+        return internal::meta_to_user_traits<Type>(fetch_node().traits);
     }
 
     /*! @copydoc meta_data::custom */
     [[nodiscard]] meta_custom custom() const noexcept {
-        return node_or_assert().details ? meta_custom{node_or_assert().details->custom} : meta_custom{};
+        return fetch_node().details ? meta_custom{fetch_node().details->custom} : meta_custom{};
     }
 
     /**
@@ -1528,11 +1527,11 @@ public:
 
     /*! @copydoc meta_data::operator== */
     [[nodiscard]] bool operator==(const meta_type &other) const noexcept {
-        return (ctx == other.ctx) && ((!*this && !other) || ((*this && other) && (info() == other.info())));
+        return (ctx == other.ctx) && (fetch_node().id == other.fetch_node().id);
     }
 
 private:
-    const internal::meta_type_node *node{};
+    mutable const internal::meta_type_node *node{};
     const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
 };
 

+ 56 - 0
test/entt/meta/meta_type.cpp

@@ -205,6 +205,62 @@ TEST_F(MetaType, Resolve) {
     ASSERT_TRUE(found);
 }
 
+TEST_F(MetaType, SafeWhenEmpty) {
+    using namespace entt::literals;
+
+    entt::meta_type type{};
+    entt::meta_any *args = nullptr;
+
+    ASSERT_FALSE(type);
+    ASSERT_EQ(type, entt::meta_type{});
+    ASSERT_EQ(type.info(), entt::type_id<void>());
+    ASSERT_EQ(type.id(), entt::type_id<void>().hash());
+    ASSERT_EQ(type.size_of(), 0u);
+    ASSERT_FALSE(type.is_arithmetic());
+    ASSERT_FALSE(type.is_integral());
+    ASSERT_FALSE(type.is_signed());
+    ASSERT_FALSE(type.is_array());
+    ASSERT_FALSE(type.is_enum());
+    ASSERT_FALSE(type.is_class());
+    ASSERT_FALSE(type.is_pointer());
+    ASSERT_EQ(type.remove_pointer(), type);
+    ASSERT_FALSE(type.is_pointer_like());
+    ASSERT_FALSE(type.is_sequence_container());
+    ASSERT_FALSE(type.is_associative_container());
+    ASSERT_FALSE(type.is_template_specialization());
+    ASSERT_EQ(type.template_arity(), 0u);
+    ASSERT_EQ(type.template_type(), type);
+    ASSERT_EQ(type.template_arg(0u), type);
+    ASSERT_EQ(type.template_arg(1u), type);
+    ASSERT_TRUE(type.can_cast(type));
+    ASSERT_TRUE(type.can_cast(entt::resolve<void>()));
+    ASSERT_TRUE(type.can_convert(type));
+    ASSERT_TRUE(type.can_convert(entt::resolve<void>()));
+    ASSERT_EQ(type.base().begin(), type.base().end());
+    ASSERT_EQ(type.data().begin(), type.data().end());
+    ASSERT_EQ(type.data("data"_hs), entt::meta_data{});
+    ASSERT_EQ(type.func().begin(), type.func().end());
+    ASSERT_EQ(type.func("func"_hs), entt::meta_func{});
+    ASSERT_FALSE(type.construct(args, 0u));
+    ASSERT_FALSE(type.construct(args, 1u));
+    ASSERT_FALSE(type.construct());
+    ASSERT_FALSE(type.construct(0.0));
+    ASSERT_FALSE(type.from_void(static_cast<void *>(nullptr)));
+    ASSERT_FALSE(type.from_void(static_cast<void *>(nullptr), true));
+    ASSERT_FALSE(type.from_void(static_cast<void *>(&type)));
+    ASSERT_FALSE(type.from_void(static_cast<void *>(&type), true));
+    ASSERT_FALSE(type.from_void(static_cast<const void *>(nullptr)));
+    ASSERT_FALSE(type.from_void(static_cast<const void *>(&type)));
+    ASSERT_FALSE(type.invoke("func"_hs, {}, args, 0u));
+    ASSERT_FALSE(type.invoke("func"_hs, {}, args, 1u));
+    ASSERT_FALSE(type.invoke("func"_hs, {}));
+    ASSERT_FALSE(type.invoke("func"_hs, {}, 'c'));
+    ASSERT_FALSE(type.set("data"_hs, {}, 0));
+    ASSERT_FALSE(type.get("data"_hs, {}));
+    ASSERT_EQ(type.traits<test::meta_traits>(), test::meta_traits::none);
+    ASSERT_EQ(static_cast<const char *>(type.custom()), nullptr);
+}
+
 TEST_F(MetaType, UserTraits) {
     ASSERT_EQ(entt::resolve<bool>().traits<test::meta_traits>(), test::meta_traits::none);
     ASSERT_EQ(entt::resolve<clazz>().traits<test::meta_traits>(), test::meta_traits::none);