Просмотр исходного кода

meta_any: added support for const references, reviewed meta_any::cast (T vs T &)

Michele Caini 5 лет назад
Родитель
Сommit
5578ea2f30

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

@@ -342,7 +342,15 @@ public:
     /*! @copydoc try_cast */
     template<typename Type>
     [[nodiscard]] Type * try_cast() {
-        return const_cast<Type *>(std::as_const(*this).try_cast<Type>());
+        if(node) {
+            if(const auto info = internal::meta_info<Type>::resolve()->info; node->info == info) {
+                return static_cast<Type *>(storage.data());
+            } else if(const auto *base = internal::find_if<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) {
+                return static_cast<Type *>(const_cast<void *>(base->cast(storage.data())));
+            }
+        }
+
+        return nullptr;
     }
 
     /**
@@ -358,16 +366,25 @@ public:
      * @return A reference to the contained instance.
      */
     template<typename Type>
-    [[nodiscard]] const Type & cast() const {
-        auto * const actual = try_cast<Type>();
+    [[nodiscard]] Type cast() const {
+        auto * const actual = try_cast<std::remove_cv_t<std::remove_reference_t<Type>>>();
         ENTT_ASSERT(actual);
-        return *actual;
+        return static_cast<Type>(*actual);
     }
 
     /*! @copydoc cast */
     template<typename Type>
-    [[nodiscard]] Type & cast() {
-        return const_cast<Type &>(std::as_const(*this).cast<Type>());
+    [[nodiscard]] Type cast() {
+        if constexpr(!std::is_reference_v<Type> || std::is_const_v<std::remove_reference_t<Type>>) {
+            // last attempt to make wrappers for const references return their values
+            auto * const actual = std::as_const(*this).try_cast<std::remove_cv_t<std::remove_reference_t<Type>>>();
+            ENTT_ASSERT(actual);
+            return static_cast<Type>(*actual);
+        } else {
+            auto * const actual = try_cast<std::remove_cv_t<std::remove_reference_t<Type>>>();
+            ENTT_ASSERT(actual);
+            return static_cast<Type>(*actual);
+        }
     }
 
     /**
@@ -1628,15 +1645,15 @@ class meta_sequence_container::meta_iterator {
 
     template<typename It>
     static void incr(meta_any any) {
-        ++any.cast<It>();
+        ++any.cast<It &>();
     }
 
     template<typename It>
     [[nodiscard]] static meta_any deref(meta_any any) {
         if constexpr(std::is_const_v<std::remove_reference_t<decltype(*std::declval<It>())>>) {
-            return *any.cast<It>();
+            return *any.cast<It &>();
         } else {
-            return std::ref(*any.cast<It>());
+            return std::ref(*any.cast<It &>());
         }
     }
 
@@ -1751,16 +1768,16 @@ struct meta_sequence_container::meta_sequence_container_proxy {
 
     [[nodiscard]] static std::pair<iterator, bool> insert(void *container, iterator it, meta_any value) {
         if(const auto *v_ptr = value.try_cast<typename traits_type::value_type>(); v_ptr || value.convert<typename traits_type::value_type>()) {
-            auto ret = traits_type::insert(*static_cast<Type *>(container), it.handle.cast<typename traits_type::iterator>(), v_ptr ? *v_ptr : value.cast<typename traits_type::value_type>());
-            return {iterator{std::move(ret.first)}, ret.second};
+            auto ret = traits_type::insert(*static_cast<Type *>(container), it.handle.cast<const typename traits_type::iterator &>(), v_ptr ? *v_ptr : value.cast<const typename traits_type::value_type &>());
+            return { iterator{std::move(ret.first)}, ret.second };
         }
 
         return {};
     }
 
     [[nodiscard]] static std::pair<iterator, bool> erase(void *container, iterator it) {
-        auto ret = traits_type::erase(*static_cast<Type *>(container), it.handle.cast<typename traits_type::iterator>());
-        return {iterator{std::move(ret.first)}, ret.second};
+        auto ret = traits_type::erase(*static_cast<Type *>(container), it.handle.cast<const typename traits_type::iterator &>());
+        return { iterator{std::move(ret.first)}, ret.second };
     }
 
     [[nodiscard]] static meta_any get(void *container, size_type pos) {
@@ -1874,15 +1891,15 @@ inline std::pair<meta_sequence_container::iterator, bool> meta_sequence_containe
 class meta_associative_container::meta_iterator {
     template<typename It>
     static void incr(meta_any any) {
-        ++any.cast<It>();
+        ++any.cast<It &>();
     }
 
     template<bool KeyOnly, typename It>
     [[nodiscard]] static meta_any key(meta_any any) {
         if constexpr(KeyOnly) {
-            return *any.cast<It>();
+            return *any.cast<It &>();
         } else {
-            return any.cast<It>()->first;
+            return any.cast<It &>()->first;
         }
     }
 
@@ -1891,7 +1908,7 @@ class meta_associative_container::meta_iterator {
         if constexpr(KeyOnly) {
             return meta_any{};
         } else {
-            return std::ref(any.cast<It>()->second);
+            return std::ref(any.cast<It &>()->second);
         }
     }
 
@@ -2019,10 +2036,10 @@ struct meta_associative_container::meta_associative_container_proxy {
     [[nodiscard]] static bool insert(void *container, meta_any key, meta_any value) {
         if(const auto *k_ptr = key.try_cast<typename traits_type::key_type>(); k_ptr || key.convert<typename traits_type::key_type>()) {
             if constexpr(is_key_only_meta_associative_container_v<Type>) {
-                return traits_type::insert(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<typename traits_type::key_type>());
+                return traits_type::insert(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>());
             } else {
-                if(auto *m_ptr = value.try_cast<typename traits_type::mapped_type>(); m_ptr || value.convert<typename traits_type::mapped_type>()) {
-                    return traits_type::insert(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<typename traits_type::key_type>(), m_ptr ? *m_ptr : value.cast<typename traits_type::mapped_type>());
+                if(const auto *m_ptr = value.try_cast<typename traits_type::mapped_type>(); m_ptr || value.convert<typename traits_type::mapped_type>()) {
+                    return traits_type::insert(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>(), m_ptr ? *m_ptr : value.cast<const typename traits_type::mapped_type &>());
                 }
             }
         }
@@ -2032,7 +2049,7 @@ struct meta_associative_container::meta_associative_container_proxy {
 
     [[nodiscard]] static bool erase(void *container, meta_any key) {
         if(const auto *k_ptr = key.try_cast<typename traits_type::key_type>(); k_ptr || key.convert<typename traits_type::key_type>()) {
-            return traits_type::erase(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<typename traits_type::key_type>());
+            return traits_type::erase(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>());
         }
 
         return false;
@@ -2040,7 +2057,7 @@ struct meta_associative_container::meta_associative_container_proxy {
 
     [[nodiscard]] static iterator find(void *container, meta_any key) {
         if(const auto *k_ptr = key.try_cast<typename traits_type::key_type>(); k_ptr || key.convert<typename traits_type::key_type>()) {
-            return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::find(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<typename traits_type::key_type>())};
+            return iterator{is_key_only_meta_associative_container<Type>{}, traits_type::find(*static_cast<Type *>(container), k_ptr ? *k_ptr : key.cast<const typename traits_type::key_type &>())};
         }
 
         return {};

+ 14 - 5
test/entt/meta/meta_any.cpp

@@ -579,11 +579,20 @@ TEST_F(MetaAny, Cast) {
 
     ASSERT_TRUE(any);
     ASSERT_EQ(any.type(), entt::resolve<fat_t>());
-    ASSERT_EQ(any.try_cast<std::size_t>(), nullptr);
-    ASSERT_NE(any.try_cast<empty_t>(), nullptr);
-    ASSERT_EQ(any.try_cast<fat_t>(), any.data());
-    ASSERT_EQ(std::as_const(any).try_cast<empty_t>(), any.try_cast<empty_t>());
-    ASSERT_EQ(std::as_const(any).try_cast<fat_t>(), any.data());
+    ASSERT_EQ(std::as_const(any).cast<const fat_t &>(), fat_t{});
+    ASSERT_EQ(any.cast<const fat_t>(), fat_t{});
+    ASSERT_EQ(any.cast<fat_t &>(), fat_t{});
+    ASSERT_EQ(any.cast<fat_t>(), fat_t{});
+
+    ASSERT_EQ(any.cast<fat_t>().gnam[0u], 0.);
+
+    any.cast<fat_t>().gnam[0u] = 3.;
+
+    ASSERT_EQ(any.cast<fat_t>().gnam[0u], 0.);
+
+    any.cast<fat_t &>().gnam[0u] = 3.;
+
+    ASSERT_EQ(any.cast<fat_t>().gnam[0u], 3.);
 }
 
 TEST_F(MetaAny, Convert) {

+ 8 - 8
test/entt/meta/meta_container.cpp

@@ -111,9 +111,9 @@ TEST_F(MetaContainer, StdVector) {
     ASSERT_EQ(view.size(), 3u);
     ASSERT_NE(view.begin(), view.end());
 
-    view[0].cast<int>() = 2;
-    view[1].cast<int>() = 3;
-    view[2].cast<int>() = 4;
+    view[0].cast<int &>() = 2;
+    view[1].cast<int &>() = 3;
+    view[2].cast<int &>() = 4;
 
     ASSERT_EQ(view[1u].cast<int>(), 3);
 
@@ -153,9 +153,9 @@ TEST_F(MetaContainer, StdArray) {
     ASSERT_FALSE(view.resize(5u));
     ASSERT_EQ(view.size(), 3u);
 
-    view[0].cast<int>() = 2;
-    view[1].cast<int>() = 3;
-    view[2].cast<int>() = 4;
+    view[0].cast<int &>() = 2;
+    view[1].cast<int &>() = 3;
+    view[2].cast<int &>() = 4;
 
     ASSERT_EQ(view[1u].cast<int>(), 3);
 
@@ -216,7 +216,7 @@ TEST_F(MetaContainer, StdMap) {
     ASSERT_EQ(view.size(), 4u);
     ASSERT_EQ(view.find(0), view.end());
 
-    (*view.find(1)).second.cast<char>() = 'f';
+    (*view.find(1)).second.cast<char &>() = 'f';
 
     ASSERT_EQ((*view.find(1)).second.cast<char>(), 'f');
 
@@ -259,7 +259,7 @@ TEST_F(MetaContainer, StdSet) {
     ASSERT_EQ(view.size(), 4u);
     ASSERT_EQ(view.find(0), view.end());
 
-    (*view.find(1)).first.cast<int>() = 42;
+    (*view.find(1)).first.cast<int &>() = 42;
 
     ASSERT_EQ((*view.find(1)).first.cast<int>(), 1);
 

+ 3 - 3
test/entt/meta/meta_data.cpp

@@ -224,7 +224,7 @@ TEST_F(MetaData, GetMetaAnyArg) {
     using namespace entt::literals;
 
     entt::meta_any any{clazz_t{}};
-    any.cast<clazz_t>().i = 99;
+    any.cast<clazz_t &>().i = 99;
     const auto value = entt::resolve<clazz_t>().data("i"_hs).get(any);
 
     ASSERT_TRUE(value);
@@ -433,8 +433,8 @@ TEST_F(MetaData, AsRef) {
     ASSERT_EQ(h_data.type(), entt::resolve<int>());
     ASSERT_EQ(i_data.type(), entt::resolve<int>());
 
-    h_data.get(instance).cast<int>() = 3;
-    i_data.get(instance).cast<int>() = 3;
+    h_data.get(instance).cast<int &>() = 3;
+    i_data.get(instance).cast<int &>() = 3;
 
     ASSERT_NE(instance.h, 3);
     ASSERT_EQ(instance.i, 3);

+ 1 - 1
test/entt/meta/meta_func.cpp

@@ -322,7 +322,7 @@ TEST_F(MetaFunc, AsRef) {
 
     func_t instance{};
     auto func = entt::resolve<func_t>().func("a"_hs);
-    func.invoke(instance).cast<int>() = 3;
+    func.invoke(instance).cast<int &>() = 3;
 
     ASSERT_EQ(func.ret(), entt::resolve<int>());
     ASSERT_EQ(instance.value, 3);

+ 3 - 3
test/entt/meta/meta_pointer.cpp

@@ -40,7 +40,7 @@ TEST(MetaPointerLike, DereferenceOperatorConstType) {
     ASSERT_FALSE(deref.type().is_pointer_like());
     ASSERT_EQ(deref.type(), entt::resolve<int>());
 
-    deref.cast<int>() = 42;
+    deref.cast<int &>() = 42;
 
     ASSERT_EQ(*any.cast<const int *>(), 0);
     ASSERT_EQ(value, 0);
@@ -61,7 +61,7 @@ TEST(MetaPointerLike, DereferenceOperatorRawPointer) {
     ASSERT_FALSE(deref.type().is_pointer_like());
     ASSERT_EQ(deref.type(), entt::resolve<int>());
 
-    deref.cast<int>() = 42;
+    deref.cast<int &>() = 42;
 
     ASSERT_EQ(*any.cast<int *>(), 42);
     ASSERT_EQ(value, 42);
@@ -82,7 +82,7 @@ TEST(MetaPointerLike, DereferenceOperatorSmartPointer) {
     ASSERT_FALSE(deref.type().is_pointer_like());
     ASSERT_EQ(deref.type(), entt::resolve<int>());
 
-    deref.cast<int>() = 42;
+    deref.cast<int &>() = 42;
 
     ASSERT_EQ(*any.cast<std::shared_ptr<int>>(), 42);
     ASSERT_EQ(*value, 42);