diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index e99184345..a68fba557 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -1393,17 +1393,31 @@ bool meta_any::set(const id_type id, Type &&value) { } [[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { - if(const auto &info = type.info(); (node && *node->info == info) || internal::find_by<&internal::meta_type_node::base>(info, node)) { + if(const auto &info = type.info(); node && *node->info == info) { return as_ref(); - } else if(const auto *const conv = internal::find_by<&internal::meta_type_node::conv>(info, node); conv) { - return conv->conv(*this); - } 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(); - ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found"); - const auto value = node->conversion_helper(nullptr, storage.data()); - other.node->conversion_helper(other.storage.data(), &value); - return other; + } else if(node) { + for(auto *it = node->conv; it; it = it->next) { + if(*it->type->info == info) { + return it->conv(*this); + } + } + + if(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(); + ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found"); + const auto value = node->conversion_helper(nullptr, storage.data()); + other.node->conversion_helper(other.storage.data(), &value); + return other; + } + + for(auto *it = node->base; it; it = it->next) { + const auto as_const = it->cast(as_ref()); + + if(auto other = as_const.allow_cast(type); other) { + return other; + } + } } return {}; diff --git a/test/entt/meta/meta_base.cpp b/test/entt/meta/meta_base.cpp index f4b4982dd..6598c2d35 100644 --- a/test/entt/meta/meta_base.cpp +++ b/test/entt/meta/meta_base.cpp @@ -11,6 +11,11 @@ struct base_1_t { struct base_2_t { base_2_t() = default; + + operator int() const { + return value_2; + } + int value_2{}; }; @@ -32,6 +37,7 @@ struct MetaBase: ::testing::Test { .data<&base_1_t::value_1>("value_1"_hs); entt::meta() + .conv() .data<&base_2_t::value_2>("value_2"_hs); entt::meta() @@ -68,7 +74,7 @@ TEST_F(MetaBase, Functionalities) { ASSERT_EQ(any.cast().value_1, as_derived.cast().value_1); } -TEST_F(MetaBase, ThisIsNotThis) { +TEST_F(MetaBase, SetGetWithMutatingThis) { using namespace entt::literals; derived_t instance; @@ -106,6 +112,44 @@ TEST_F(MetaBase, ThisIsNotThis) { ASSERT_EQ(instance.value_3, 3); } +TEST_F(MetaBase, ConvWithMutatingThis) { + entt::meta_any any{derived_t{}}; + auto as_cref = std::as_const(any).as_ref(); + any.cast().value_2 = 42; + + auto conv = std::as_const(any).allow_cast(); + auto from_cref = std::as_const(as_cref).allow_cast(); + + ASSERT_TRUE(conv); + ASSERT_TRUE(from_cref); + ASSERT_EQ(conv.cast(), 42); + ASSERT_EQ(from_cref.cast(), 42); + + ASSERT_TRUE(any.allow_cast()); + ASSERT_TRUE(as_cref.allow_cast()); + ASSERT_EQ(any.cast(), 42); + ASSERT_EQ(as_cref.cast(), 42); +} + +TEST_F(MetaBase, OpaqueConvWithMutatingThis) { + entt::meta_any any{derived_t{}}; + auto as_cref = std::as_const(any).as_ref(); + any.cast().value_2 = 42; + + auto conv = std::as_const(any).allow_cast(entt::resolve()); + auto from_cref = std::as_const(as_cref).allow_cast(entt::resolve()); + + ASSERT_TRUE(conv); + ASSERT_TRUE(from_cref); + ASSERT_EQ(conv.cast(), 42); + ASSERT_EQ(from_cref.cast(), 42); + + ASSERT_TRUE(any.allow_cast(entt::resolve())); + ASSERT_TRUE(as_cref.allow_cast(entt::resolve())); + ASSERT_EQ(any.cast(), 42); + ASSERT_EQ(as_cref.cast(), 42); +} + TEST_F(MetaBase, ReRegistration) { SetUp();