Browse Source

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

Michele Caini 4 years ago
parent
commit
6030f56c76
2 changed files with 93 additions and 17 deletions
  1. 14 6
      src/entt/meta/meta.hpp
  2. 79 11
      test/entt/meta/meta_base.cpp

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

@@ -350,9 +350,14 @@ public:
     [[nodiscard]] const Type *try_cast() const {
         if(const auto &info = type_id<Type>(); node && *node->info == info) {
             return any_cast<Type>(&storage);
-        } else if(const auto *base = internal::find_by<&internal::meta_type_node::base>(info, node); base) {
-            const auto as_const_base = base->cast(as_ref());
-            return any_cast<Type>(&as_const_base.storage);
+        } else if(node) {
+            for(auto *it = node->base; it; it = it->next) {
+                const auto as_const = it->cast(as_ref());
+
+                if(const Type *base = as_const.try_cast<Type>(); base) {
+                    return base;
+                }
+            }
         }
 
         return nullptr;
@@ -363,9 +368,12 @@ public:
     [[nodiscard]] Type *try_cast() {
         if(const auto &info = type_id<Type>(); node && *node->info == info) {
             return any_cast<Type>(&storage);
-        } else if(const auto *base = internal::find_by<&internal::meta_type_node::base>(info, node); base) {
-            auto as_base = base->cast(as_ref());
-            return any_cast<Type>(&as_base.storage);
+        } else if(node) {
+            for(auto *it = node->base; it; it = it->next) {
+                if(Type *base = it->cast(as_ref()).try_cast<Type>(); base) {
+                    return base;
+                }
+            }
         }
 
         return nullptr;

+ 79 - 11
test/entt/meta/meta_base.cpp

@@ -4,22 +4,45 @@
 #include <entt/meta/node.hpp>
 #include <entt/meta/resolve.hpp>
 
-struct base_t {
-    base_t() = default;
-    int value;
+struct base_1_t {
+    base_1_t() = default;
+    int value_1{};
 };
 
-struct derived_t: base_t {
+struct base_2_t {
+    base_2_t() = default;
+    int value_2{};
+};
+
+struct base_3_t: base_2_t {
+    base_3_t() = default;
+    int value_3{};
+};
+
+struct derived_t: base_1_t, base_3_t {
     derived_t() = default;
+    int value{};
 };
 
 struct MetaBase: ::testing::Test {
     void SetUp() override {
         using namespace entt::literals;
 
+        entt::meta<base_1_t>()
+            .data<&base_1_t::value_1>("value_1"_hs);
+
+        entt::meta<base_2_t>()
+            .data<&base_2_t::value_2>("value_2"_hs);
+
+        entt::meta<base_3_t>()
+            .base<base_2_t>()
+            .data<&base_3_t::value_3>("value_3"_hs);
+
         entt::meta<derived_t>()
             .type("derived"_hs)
-            .base<base_t>();
+            .base<base_1_t>()
+            .base<base_3_t>()
+            .data<&derived_t::value>("value"_hs);
     }
 
     void TearDown() override {
@@ -29,20 +52,58 @@ struct MetaBase: ::testing::Test {
 
 TEST_F(MetaBase, Functionalities) {
     auto any = entt::resolve<derived_t>().construct();
-    any.cast<derived_t &>().value = 42;
+    any.cast<derived_t &>().value_1 = 42;
     auto as_derived = any.as_ref();
 
-    ASSERT_TRUE(any.allow_cast<base_t &>());
+    ASSERT_TRUE(any.allow_cast<base_1_t &>());
 
     ASSERT_FALSE(any.allow_cast<char>());
     ASSERT_FALSE(as_derived.allow_cast<char>());
 
     ASSERT_TRUE(any);
-    ASSERT_EQ(any.cast<base_t &>().value, as_derived.cast<derived_t &>().value);
+    ASSERT_EQ(any.cast<base_1_t &>().value_1, as_derived.cast<derived_t &>().value_1);
+
+    any.cast<base_1_t &>().value_1 = 3;
+
+    ASSERT_EQ(any.cast<const base_1_t &>().value_1, as_derived.cast<const derived_t &>().value_1);
+}
+
+TEST_F(MetaBase, ThisIsNotThis) {
+    using namespace entt::literals;
+
+    derived_t instance;
+    auto any = entt::forward_as_meta(instance);
+    auto as_cref = std::as_const(any).as_ref();
+
+    ASSERT_NE(static_cast<const void *>(static_cast<const base_1_t *>(&instance)), static_cast<const void *>(static_cast<const base_2_t *>(&instance)));
+    ASSERT_NE(static_cast<const void *>(static_cast<const base_1_t *>(&instance)), static_cast<const void *>(static_cast<const base_3_t *>(&instance)));
+    ASSERT_EQ(static_cast<const void *>(static_cast<const base_2_t *>(&instance)), static_cast<const void *>(static_cast<const base_3_t *>(&instance)));
+    ASSERT_EQ(static_cast<const void *>(&instance), static_cast<const void *>(static_cast<const base_1_t *>(&instance)));
+
+    ASSERT_TRUE(any.set("value"_hs, 42));
+    ASSERT_TRUE(any.set("value_1"_hs, 1));
+    ASSERT_TRUE(any.set("value_2"_hs, 2));
+    ASSERT_TRUE(any.set("value_3"_hs, 3));
 
-    any.cast<base_t &>().value = 3;
+    ASSERT_FALSE(any.type().set("value"_hs, as_cref, 0));
+    ASSERT_FALSE(any.type().set("value_1"_hs, as_cref, 0));
+    ASSERT_FALSE(any.type().set("value_2"_hs, as_cref, 0));
+    ASSERT_FALSE(any.type().set("value_3"_hs, as_cref, 0));
 
-    ASSERT_EQ(any.cast<const base_t &>().value, as_derived.cast<const derived_t &>().value);
+    ASSERT_EQ(any.get("value"_hs).cast<int>(), 42);
+    ASSERT_EQ(any.get("value_1"_hs).cast<const int>(), 1);
+    ASSERT_EQ(any.get("value_2"_hs).cast<int>(), 2);
+    ASSERT_EQ(any.get("value_3"_hs).cast<const int>(), 3);
+
+    ASSERT_EQ(as_cref.get("value"_hs).cast<const int>(), 42);
+    ASSERT_EQ(as_cref.get("value_1"_hs).cast<int>(), 1);
+    ASSERT_EQ(as_cref.get("value_2"_hs).cast<const int>(), 2);
+    ASSERT_EQ(as_cref.get("value_3"_hs).cast<int>(), 3);
+
+    ASSERT_EQ(instance.value, 42);
+    ASSERT_EQ(instance.value_1, 1);
+    ASSERT_EQ(instance.value_2, 2);
+    ASSERT_EQ(instance.value_3, 3);
 }
 
 TEST_F(MetaBase, ReRegistration) {
@@ -51,5 +112,12 @@ TEST_F(MetaBase, ReRegistration) {
     auto *node = entt::internal::meta_node<derived_t>::resolve();
 
     ASSERT_NE(node->base, nullptr);
-    ASSERT_EQ(node->base->next, nullptr);
+    ASSERT_NE(node->base->type->base, nullptr);
+    ASSERT_EQ(node->base->type->base->next, nullptr);
+    ASSERT_EQ(node->base->type->base->type->base, nullptr);
+
+    ASSERT_NE(node->base->next, nullptr);
+    ASSERT_EQ(node->base->next->type->base, nullptr);
+
+    ASSERT_EQ(node->base->next->next, nullptr);
 }