Browse Source

meta: make meta_any::allow_cast work with mutating this pointers

Michele Caini 4 years ago
parent
commit
e835ce0697
2 changed files with 69 additions and 11 deletions
  1. 24 10
      src/entt/meta/meta.hpp
  2. 45 1
      test/entt/meta/meta_base.cpp

+ 24 - 10
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 {};

+ 45 - 1
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<base_2_t>()
+            .conv<int>()
             .data<&base_2_t::value_2>("value_2"_hs);
 
         entt::meta<base_3_t>()
@@ -68,7 +74,7 @@ TEST_F(MetaBase, Functionalities) {
     ASSERT_EQ(any.cast<const base_1_t &>().value_1, as_derived.cast<const derived_t &>().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<derived_t &>().value_2 = 42;
+
+    auto conv = std::as_const(any).allow_cast<int>();
+    auto from_cref = std::as_const(as_cref).allow_cast<int>();
+
+    ASSERT_TRUE(conv);
+    ASSERT_TRUE(from_cref);
+    ASSERT_EQ(conv.cast<int>(), 42);
+    ASSERT_EQ(from_cref.cast<int>(), 42);
+
+    ASSERT_TRUE(any.allow_cast<int>());
+    ASSERT_TRUE(as_cref.allow_cast<int>());
+    ASSERT_EQ(any.cast<int>(), 42);
+    ASSERT_EQ(as_cref.cast<int>(), 42);
+}
+
+TEST_F(MetaBase, OpaqueConvWithMutatingThis) {
+    entt::meta_any any{derived_t{}};
+    auto as_cref = std::as_const(any).as_ref();
+    any.cast<derived_t &>().value_2 = 42;
+
+    auto conv = std::as_const(any).allow_cast(entt::resolve<int>());
+    auto from_cref = std::as_const(as_cref).allow_cast(entt::resolve<int>());
+
+    ASSERT_TRUE(conv);
+    ASSERT_TRUE(from_cref);
+    ASSERT_EQ(conv.cast<int>(), 42);
+    ASSERT_EQ(from_cref.cast<int>(), 42);
+
+    ASSERT_TRUE(any.allow_cast(entt::resolve<int>()));
+    ASSERT_TRUE(as_cref.allow_cast(entt::resolve<int>()));
+    ASSERT_EQ(any.cast<int>(), 42);
+    ASSERT_EQ(as_cref.cast<int>(), 42);
+}
+
 TEST_F(MetaBase, ReRegistration) {
     SetUp();