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

meta_any: support assigning the value wrapped by a meta_any

Michele Caini 4 лет назад
Родитель
Сommit
8b1a3849e6
4 измененных файлов с 327 добавлено и 29 удалено
  1. 7 1
      src/entt/meta/factory.hpp
  2. 59 22
      src/entt/meta/meta.hpp
  3. 16 6
      src/entt/meta/node.hpp
  4. 245 0
      test/entt/meta/meta_any.cpp

+ 7 - 1
src/entt/meta/factory.hpp

@@ -239,7 +239,13 @@ public:
         static internal::meta_base_node node{
             nullptr,
             internal::meta_node<Base>::resolve(),
-            [](const void *instance) ENTT_NOEXCEPT -> const void * { return static_cast<const Base *>(static_cast<const Type *>(instance)); }
+            [](meta_any other) ENTT_NOEXCEPT -> meta_any {
+                if(auto *data = other.data(); data) {
+                    return entt::forward_as_meta(*static_cast<Base *>(static_cast<Type *>(data)));
+                }
+
+                return entt::forward_as_meta(*static_cast<const Base *>(static_cast<const Type *>(std::as_const(other).data())));
+            }
             // tricks clang-format
         };
 

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

@@ -350,8 +350,9 @@ 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::visit<&internal::meta_type_node::base>([info](const auto *curr) { return *curr->type->info == info; }, node); base) {
-            return static_cast<const Type *>(base->cast(storage.data()));
+        } 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);
         }
 
         return nullptr;
@@ -362,8 +363,9 @@ 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::visit<&internal::meta_type_node::base>([info](const auto *curr) { return *curr->type->info == info; }, node); base) {
-            return static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(base->cast(static_cast<constness_as_t<any, Type> &>(storage).data())));
+        } 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);
         }
 
         return nullptr;
@@ -375,7 +377,7 @@ public:
      * The type of the instance must be such that the cast is possible.
      *
      * @warning
-     * Attempting to perform an invalid cast results in undefined behavior.
+     * Attempting to perform an invalid cast results is undefined behavior.
      *
      * @tparam Type Type to which to cast the instance.
      * @return A reference to the contained instance.
@@ -445,20 +447,16 @@ public:
      */
     template<typename Type>
     bool allow_cast() {
-        if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) {
-            if(auto other = std::as_const(*this).allow_cast(internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve()); other) {
-                if(other.storage.owner()) {
-                    std::swap(*this, other);
-                    return true;
-                }
-
-                return (storage.data() != nullptr);
+        if(auto other = std::as_const(*this).allow_cast(internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve()); other) {
+            if(other.storage.owner()) {
+                std::swap(*this, other);
+                return true;
             }
 
-            return false;
-        } else {
-            return allow_cast(internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve());
+            return (static_cast<constness_as_t<any, std::remove_reference_t<const Type>> &>(storage).data() != nullptr);
         }
+
+        return false;
     }
 
     /**
@@ -475,6 +473,20 @@ public:
         node = internal::meta_node<std::remove_const_t<std::remove_reference_t<Type>>>::resolve();
     }
 
+    /**
+     * @brief Copy assigns a value to the contained object without replacing it.
+     * @param other The value to assign to the contained object.
+     * @return True in case of success, false otherwise.
+     */
+    bool assign(const meta_any &other);
+
+    /**
+     * @brief Move assigns a value to the contained object without replacing it.
+     * @param other The value to assign to the contained object.
+     * @return True in case of success, false otherwise.
+     */
+    bool assign(meta_any &&other);
+
     /*! @brief Destroys contained object */
     void reset() {
         release();
@@ -1138,7 +1150,7 @@ public:
      * @return The registered base meta type for the given identifier, if any.
      */
     [[nodiscard]] meta_type base(const id_type id) const {
-        return internal::visit<&node_type::base>([id](const auto *curr) { return curr->type->id == id; }, node);
+        return internal::find_by<&node_type::base>(id, node);
     }
 
     /**
@@ -1158,7 +1170,7 @@ public:
      * @return The registered meta data for the given identifier, if any.
      */
     [[nodiscard]] meta_data data(const id_type id) const {
-        return internal::visit<&node_type::data>([id](const auto *curr) { return curr->id == id; }, node);
+        return internal::find_by<&node_type::data>(id, node);
     }
 
     /**
@@ -1180,7 +1192,7 @@ public:
      * @return The registered meta function for the given identifier, if any.
      */
     [[nodiscard]] meta_func func(const id_type id) const {
-        return internal::visit<&node_type::func>([id](const auto *curr) { return curr->id == id; }, node);
+        return internal::find_by<&node_type::func>(id, node);
     }
 
     /**
@@ -1304,7 +1316,7 @@ public:
      * @return The registered meta property for the given key, if any.
      */
     [[nodiscard]] meta_prop prop(meta_any key) const {
-        return internal::visit<&internal::meta_type_node::prop>([&key](const auto *curr) { return curr->id == key; }, node);
+        return internal::find_by<&internal::meta_type_node::prop>(key, node);
     }
 
     /**
@@ -1366,9 +1378,9 @@ 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::visit<&internal::meta_type_node::base>([info](const auto *curr) { return *curr->type->info == info; }, node)) {
+    if(const auto &info = type.info(); (node && *node->info == info) || internal::find_by<&internal::meta_type_node::base>(info, node)) {
         return as_ref();
-    } else if(const auto *const conv = internal::visit<&internal::meta_type_node::conv>([info](const auto *curr) { return *curr->type->info == info; }, node); conv) {
+    } else if(const auto *const conv = internal::find_by<&internal::meta_type_node::conv>(info, node); conv) {
         return conv->conv(storage.data());
     } else if(node && node->conversion_helper && (type.is_arithmetic() || type.is_enum())) {
         // exploits the fact that arithmetic types and enums are also default constructible
@@ -1382,6 +1394,31 @@ bool meta_any::set(const id_type id, Type &&value) {
     return {};
 }
 
+inline bool meta_any::assign(const meta_any &other) {
+    if(const auto value = other.allow_cast(node); value) {
+        if(*value.node->info == *node->info) {
+            return storage.assign(value.storage);
+        } else if(auto *base = internal::find_by<&internal::meta_type_node::base>(*node->info, value.node); base) {
+            const auto as_const_base = base->cast(as_ref());
+            return storage.assign(as_const_base.storage);
+        }
+    }
+
+    return false;
+}
+
+inline bool meta_any::assign(meta_any &&other) {
+    if(other.allow_cast(node)) {
+        if(*other.node->info == *node->info) {
+            return storage.assign(std::move(other.storage));
+        } else if(auto *base = internal::find_by<&internal::meta_type_node::base>(*node->info, other.node); base) {
+            return storage.assign(base->cast(other.as_ref()).storage);
+        }
+    }
+
+    return false;
+}
+
 [[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT {
     return node->type;
 }

+ 16 - 6
src/entt/meta/node.hpp

@@ -51,7 +51,7 @@ struct meta_prop_node {
 struct meta_base_node {
     meta_base_node *next;
     meta_type_node *const type;
-    const void *(*const cast)(const void *)ENTT_NOEXCEPT;
+    meta_any(*const cast)(meta_any)ENTT_NOEXCEPT;
 };
 
 struct meta_conv_node {
@@ -196,20 +196,30 @@ template<typename... Args>
     return args[index + 1u];
 }
 
-template<auto Member, typename Op>
-[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> visit(const Op &op, const internal::meta_type_node *node) {
+template<auto Member, typename Type>
+[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) {
     if(!node) {
         return nullptr;
     }
 
     for(auto *curr = node->*Member; curr; curr = curr->next) {
-        if(op(curr)) {
-            return curr;
+        if constexpr(std::is_same_v<Type, type_info>) {
+            if(*curr->type->info == info_or_id) {
+                return curr;
+            }
+        } else if constexpr(std::is_same_v<decltype(curr), meta_base_node *>) {
+            if(curr->type->id == info_or_id) {
+                return curr;
+            }
+        } else {
+            if(curr->id == info_or_id) {
+                return curr;
+            }
         }
     }
 
     for(auto *curr = node->base; curr; curr = curr->next) {
-        if(auto *ret = visit<Member>(op, curr->type); ret) {
+        if(auto *ret = find_by<Member>(info_or_id, curr->type); ret) {
             return ret;
         }
     }

+ 245 - 0
test/entt/meta/meta_any.cpp

@@ -288,6 +288,121 @@ TEST_F(MetaAny, SBODirectAssignment) {
     ASSERT_NE(entt::meta_any{0}, any);
 }
 
+TEST_F(MetaAny, SBOAssignValue) {
+    entt::meta_any any{42};
+    entt::meta_any other{3};
+    entt::meta_any invalid{empty_t{}};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_TRUE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(any.cast<int>(), 3);
+}
+
+TEST_F(MetaAny, SBOConvertAssignValue) {
+    entt::meta_any any{42};
+    entt::meta_any other{3.5};
+    entt::meta_any invalid{empty_t{}};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_TRUE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(any.cast<int>(), 3);
+}
+
+TEST_F(MetaAny, SBOAsRefAssignValue) {
+    int value = 42;
+    entt::meta_any any{entt::forward_as_meta(value)};
+    entt::meta_any other{3};
+    entt::meta_any invalid{empty_t{}};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_TRUE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(any.cast<int>(), 3);
+    ASSERT_EQ(value, 3);
+}
+
+TEST_F(MetaAny, SBOAsConstRefAssignValue) {
+    const int value = 42;
+    entt::meta_any any{entt::forward_as_meta(value)};
+    entt::meta_any other{3};
+    entt::meta_any invalid{empty_t{}};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_FALSE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(any.cast<int>(), 42);
+    ASSERT_EQ(value, 42);
+}
+
+TEST_F(MetaAny, SBOTransferValue) {
+    entt::meta_any any{42};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_TRUE(any.assign(3));
+    ASSERT_FALSE(any.assign(empty_t{}));
+    ASSERT_EQ(any.cast<int>(), 3);
+}
+
+TEST_F(MetaAny, SBOTransferConstValue) {
+    const int value = 3;
+    entt::meta_any any{42};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_TRUE(any.assign(entt::forward_as_meta(value)));
+    ASSERT_EQ(any.cast<int>(), 3);
+}
+
+TEST_F(MetaAny, SBOConvertTransferValue) {
+    entt::meta_any any{42};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_TRUE(any.assign(3.5));
+    ASSERT_FALSE(any.assign(empty_t{}));
+    ASSERT_EQ(any.cast<int>(), 3);
+}
+
+TEST_F(MetaAny, SBOAsRefTransferValue) {
+    int value = 42;
+    entt::meta_any any{entt::forward_as_meta(value)};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_TRUE(any.assign(3));
+    ASSERT_FALSE(any.assign(empty_t{}));
+    ASSERT_EQ(any.cast<int>(), 3);
+    ASSERT_EQ(value, 3);
+}
+
+TEST_F(MetaAny, SBOAsConstRefTransferValue) {
+    const int value = 42;
+    entt::meta_any any{entt::forward_as_meta(value)};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<int>(), 42);
+
+    ASSERT_FALSE(any.assign(3));
+    ASSERT_FALSE(any.assign(empty_t{}));
+    ASSERT_EQ(any.cast<int>(), 42);
+    ASSERT_EQ(value, 42);
+}
+
 TEST_F(MetaAny, NoSBOInPlaceTypeConstruction) {
     fat_t instance{.1, .2, .3, .4};
     entt::meta_any any{std::in_place_type<fat_t>, instance};
@@ -434,6 +549,136 @@ TEST_F(MetaAny, NoSBODirectAssignment) {
     ASSERT_NE(fat_t{}, any);
 }
 
+TEST_F(MetaAny, NoSBOAssignValue) {
+    entt::meta_any any{fat_t{.1, .2, .3, .4}};
+    entt::meta_any other{fat_t{.0, .1, .2, .3}};
+    entt::meta_any invalid{'c'};
+
+    const void *addr = std::as_const(any).data();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+
+    ASSERT_TRUE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.0, .1, .2, .3}));
+    ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(MetaAny, NoSBOConvertAssignValue) {
+    entt::meta_any any{empty_t{}};
+    entt::meta_any other{fat_t{.0, .1, .2, .3}};
+    entt::meta_any invalid{'c'};
+
+    const void *addr = std::as_const(any).data();
+
+    ASSERT_TRUE(any);
+    ASSERT_TRUE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(MetaAny, NoSBOAsRefAssignValue) {
+    fat_t instance{.1, .2, .3, .4};
+    entt::meta_any any{entt::forward_as_meta(instance)};
+    entt::meta_any other{fat_t{.0, .1, .2, .3}};
+    entt::meta_any invalid{'c'};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+
+    ASSERT_TRUE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.0, .1, .2, .3}));
+    ASSERT_EQ(instance, (fat_t{.0, .1, .2, .3}));
+}
+
+TEST_F(MetaAny, NoSBOAsConstRefAssignValue) {
+    const fat_t instance{.1, .2, .3, .4};
+    entt::meta_any any{entt::forward_as_meta(instance)};
+    entt::meta_any other{fat_t{.0, .1, .2, .3}};
+    entt::meta_any invalid{'c'};
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+
+    ASSERT_FALSE(any.assign(other));
+    ASSERT_FALSE(any.assign(invalid));
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+    ASSERT_EQ(instance, (fat_t{.1, .2, .3, .4}));
+}
+
+TEST_F(MetaAny, NoSBOTransferValue) {
+    entt::meta_any any{fat_t{.1, .2, .3, .4}};
+
+    const void *addr = std::as_const(any).data();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+
+    ASSERT_TRUE(any.assign(fat_t{.0, .1, .2, .3}));
+    ASSERT_FALSE(any.assign('c'));
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.0, .1, .2, .3}));
+    ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(MetaAny, NoSBOTransferConstValue) {
+    const fat_t instance{.0, .1, .2, .3};
+    entt::meta_any any{fat_t{.1, .2, .3, .4}};
+
+    const void *addr = std::as_const(any).data();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+
+    ASSERT_TRUE(any.assign(entt::forward_as_meta(instance)));
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.0, .1, .2, .3}));
+    ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(MetaAny, NoSBOConvertTransferValue) {
+    entt::meta_any any{empty_t{}};
+
+    const void *addr = std::as_const(any).data();
+
+    ASSERT_TRUE(any);
+    ASSERT_TRUE(any.assign(fat_t{.0, .1, .2, .3}));
+    ASSERT_FALSE(any.assign('c'));
+    ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(MetaAny, NoSBOAsRefTransferValue) {
+    fat_t instance{.1, .2, .3, .4};
+    entt::meta_any any{entt::forward_as_meta(instance)};
+
+    const void *addr = std::as_const(any).data();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+
+    ASSERT_TRUE(any.assign(fat_t{.0, .1, .2, .3}));
+    ASSERT_FALSE(any.assign('c'));
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.0, .1, .2, .3}));
+    ASSERT_EQ(instance, (fat_t{.0, .1, .2, .3}));
+    ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(MetaAny, NoSBOAsConstRefTransferValue) {
+    const fat_t instance{.1, .2, .3, .4};
+    entt::meta_any any{entt::forward_as_meta(instance)};
+
+    const void *addr = std::as_const(any).data();
+
+    ASSERT_TRUE(any);
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+
+    ASSERT_FALSE(any.assign(fat_t{.0, .1, .2, .3}));
+    ASSERT_FALSE(any.assign('c'));
+    ASSERT_EQ(any.cast<const fat_t &>(), (fat_t{.1, .2, .3, .4}));
+    ASSERT_EQ(instance, (fat_t{.1, .2, .3, .4}));
+    ASSERT_EQ(addr, std::as_const(any).data());
+}
+
 TEST_F(MetaAny, VoidInPlaceTypeConstruction) {
     entt::meta_any any{std::in_place_type<void>};