From 167ec4b310d2ec3465626ebc54e7ef7133fdedc8 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Wed, 8 Jan 2025 08:49:59 +0100 Subject: [PATCH] meta: safer meta_type --- TODO | 2 +- src/entt/meta/meta.hpp | 8 +++--- test/entt/meta/meta_type.cpp | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/TODO b/TODO index 8638a20eb..5c143a310 100644 --- a/TODO +++ b/TODO @@ -42,5 +42,5 @@ TODO: * sparse_set shrink_to_fit argument for sparse array shrink policy (none, empty, deep, whatever) * any cdynamic to support const ownership construction * track meta context on meta elements -* safer meta_type (no blind indirections) * no context-less meta_any{} from meta objects if possible +* auto-context in meta objects to avoid checks on contexts diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index 380b6f750..8eb5999b3 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -1152,7 +1152,7 @@ class meta_type { * @return The type info object of the underlying type. */ [[nodiscard]] const type_info &info() const noexcept { - return *node.info; + return node.info ? *node.info : type_id(); } /** @@ -1234,7 +1234,7 @@ class meta_type { * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const noexcept { - return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; + return (ctx != nullptr) ? meta_type{*ctx, node.remove_pointer(internal::meta_context::from(*ctx))} : *this; } /** @@ -1303,7 +1303,7 @@ class meta_type { */ [[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 && (internal::try_cast(internal::meta_context::from(*ctx), node, *other.node.info, this) != nullptr); + return (ctx != nullptr) && other && (internal::try_cast(internal::meta_context::from(*ctx), node, *other.node.info, this) != nullptr); } /** @@ -1312,7 +1312,7 @@ class meta_type { * @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, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast(args), 1) + ... + 0u); }) != 0u); + return (ctx != nullptr) && (internal::try_convert(internal::meta_context::from(*ctx), node, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast(args), 1) + ... + 0u); }) != 0u); } /** diff --git a/test/entt/meta/meta_type.cpp b/test/entt/meta/meta_type.cpp index d1b6515b0..9defb4011 100644 --- a/test/entt/meta/meta_type.cpp +++ b/test/entt/meta/meta_type.cpp @@ -204,6 +204,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()); + ASSERT_EQ(type.id(), entt::id_type{}); + 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_FALSE(type.can_cast(type)); + ASSERT_FALSE(type.can_cast(entt::resolve())); + ASSERT_FALSE(type.can_convert(type)); + ASSERT_FALSE(type.can_convert(entt::resolve())); + 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(nullptr))); + ASSERT_FALSE(type.from_void(static_cast(nullptr), true)); + ASSERT_FALSE(type.from_void(static_cast(&type))); + ASSERT_FALSE(type.from_void(static_cast(&type), true)); + ASSERT_FALSE(type.from_void(static_cast(nullptr))); + ASSERT_FALSE(type.from_void(static_cast(&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::none); + ASSERT_EQ(static_cast(type.custom()), nullptr); +} + TEST_F(MetaType, UserTraits) { ASSERT_EQ(entt::resolve().traits(), test::meta_traits::none); ASSERT_EQ(entt::resolve().traits(), test::meta_traits::none);