Explorar o código

meta_any meets void

Michele Caini %!s(int64=6) %!d(string=hai) anos
pai
achega
260b9a8d0d
Modificáronse 2 ficheiros con 227 adicións e 76 borrados
  1. 96 76
      src/entt/meta/meta.hpp
  2. 131 0
      test/entt/meta/meta.cpp

+ 96 - 76
src/entt/meta/meta.hpp

@@ -303,73 +303,109 @@ class meta_any {
     friend class meta_handle;
 
     using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
-    using compare_fn_type = bool(const void *, const void *) ENTT_NOEXCEPT;
+    using compare_fn_type = bool(const void *, const void *);
     using copy_fn_type = void *(storage_type &, const void *);
     using destroy_fn_type = void(storage_type &);
-    using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *, void *)  ENTT_NOEXCEPT;
+    using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT;
 
-    template<typename Type>
-    static auto compare(int, const Type &lhs, const Type &rhs) ENTT_NOEXCEPT
-    -> decltype(lhs == rhs, bool{}) {
-        return lhs == rhs;
-    }
+    template<typename Type, typename = std::void_t<>>
+    struct type_traits {
+        using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
 
-    template<typename Type>
-    static bool compare(char, const Type &lhs, const Type &rhs) ENTT_NOEXCEPT {
-        return &lhs == &rhs;
-    }
+        template<typename... Args>
+        static void * instance(storage_type &storage, Args &&... args) {
+            auto chunk = std::make_unique<chunk_type>();
+            auto *instance = new (chunk.get()) Type{std::forward<Args>(args)...};
+            new (&storage) chunk_type *{chunk.get()};
+            chunk.release();
+            return instance;
+        }
 
-    template<typename Type>
-    static bool compare(const void *lhs, const void *rhs) ENTT_NOEXCEPT {
-        return compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
-    }
+        static void destroy(storage_type &storage) {
+            auto *node = internal::meta_info<Type>::resolve();
+            auto *chunk = *reinterpret_cast<chunk_type **>(&storage);
+            auto *instance = reinterpret_cast<Type *>(chunk);
+            node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
+            delete chunk;
+        }
 
-    template<typename Type>
-    static void * copy_storage(storage_type &storage, const void *instance) {
-        return new (&storage) Type{*static_cast<const Type *>(instance)};
-    }
+        static void * copy(storage_type &storage, const void *instance) {
+            auto chunk = std::make_unique<chunk_type>();
+            new (&storage) chunk_type *{chunk.get()};
+            auto *other = new (chunk.get()) Type{*static_cast<const Type *>(instance)};
+            chunk.release();
+            return other;
+        }
 
-    template<typename Type>
-    static void * copy_object(storage_type &storage, const void *instance) {
-        using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
-        auto chunk = std::make_unique<chunk_type>();
-        new (&storage) chunk_type *{chunk.get()};
-        auto *other = new (chunk.get()) Type{*static_cast<const Type *>(instance)};
-        chunk.release();
-        return other;
-    }
+        static void * steal(storage_type &to, storage_type &from, destroy_fn_type *, void *instance) ENTT_NOEXCEPT {
+            auto *chunk = *reinterpret_cast<chunk_type **>(&from);
+            new (&to) chunk_type *{chunk};
+            chunk->~chunk_type();
+            return instance;
+        }
+
+        static bool compare(const void *lhs, const void *rhs) {
+            return meta_any::compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
+        }
+    };
 
     template<typename Type>
-    static void * steal_storage(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn, void *) noexcept {
-        void *instance = new (&to) Type{std::move(*reinterpret_cast<Type *>(&from))};
-        destroy_fn(from);
-        return instance;
-    }
+    struct type_traits<Type, std::enable_if_t<std::is_void_v<Type>>> {
+        static void * instance(storage_type &) {
+            return nullptr;
+        }
+
+        static void destroy(storage_type &) {}
+
+        static void * copy(storage_type &, const void *) {
+            return nullptr;
+        }
+
+        static void * steal(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT {
+            return nullptr;
+        }
+
+        static bool compare(const void *, const void *) {
+            return true;
+        }
+    };
 
     template<typename Type>
-    static void * steal_object(storage_type &to, storage_type &from, destroy_fn_type *, void *instance) noexcept {
-        using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
-        auto *chunk = *reinterpret_cast<chunk_type **>(&from);
-        new (&to) chunk_type *{chunk};
-        chunk->~chunk_type();
-        return instance;
-    }
+    struct type_traits<Type, std::enable_if_t<sizeof(Type) <= sizeof(void *) && std::is_nothrow_move_constructible_v<Type>>> {
+        template<typename... Args>
+        static void * instance(storage_type &storage, Args &&... args) {
+            return new (&storage) Type{std::forward<Args>(args)...};
+        }
+
+        static void destroy(storage_type &storage) {
+            auto *node = internal::meta_info<Type>::resolve();
+            auto *instance = reinterpret_cast<Type *>(&storage);
+            node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
+        }
+
+        static void * copy(storage_type &storage, const void *instance) {
+            return new (&storage) Type{*static_cast<const Type *>(instance)};
+        }
+
+        static void * steal(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn, void *) ENTT_NOEXCEPT {
+            void *instance = new (&to) Type{std::move(*reinterpret_cast<Type *>(&from))};
+            destroy_fn(from);
+            return instance;
+        }
+
+        static bool compare(const void *lhs, const void *rhs) {
+            return meta_any::compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
+        }
+    };
 
     template<typename Type>
-    static void destroy_storage(storage_type &storage) {
-        auto *node = internal::meta_info<Type>::resolve();
-        auto *instance = reinterpret_cast<Type *>(&storage);
-        node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
+    static auto compare(int, const Type &lhs, const Type &rhs) -> decltype(lhs == rhs, bool{}) {
+        return lhs == rhs;
     }
 
     template<typename Type>
-    static void destroy_object(storage_type &storage) {
-        using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
-        auto *node = internal::meta_info<Type>::resolve();
-        auto *chunk = *reinterpret_cast<chunk_type **>(&storage);
-        auto *instance = reinterpret_cast<Type *>(chunk);
-        node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
-        delete chunk;
+    static bool compare(char, const Type &lhs, const Type &rhs) {
+        return &lhs == &rhs;
     }
 
 public:
@@ -401,30 +437,14 @@ public:
     template<typename Type, typename... Args>
     meta_any(std::in_place_type_t<Type>, Args &&... args) {
         using actual_type = std::remove_cv_t<std::remove_reference_t<Type>>;
-        node = internal::meta_info<Type>::resolve();
-
-        compare_fn = &compare<actual_type>;
-
-        constexpr auto sbo_allowed = sizeof(actual_type) <= sizeof(void *)
-                && std::is_nothrow_move_constructible_v<actual_type>;
-
-        if constexpr(sbo_allowed) {
-            instance = new (&storage) actual_type{std::forward<Args>(args)...};
-            destroy_fn = &destroy_storage<actual_type>;
-            copy_fn = &copy_storage<actual_type>;
-            steal_fn = &steal_storage<actual_type>;
-        } else {
-            using chunk_type = std::aligned_storage_t<sizeof(actual_type), alignof(actual_type)>;
+        using traits_type = type_traits<actual_type>;
 
-            auto chunk = std::make_unique<chunk_type>();
-            instance = new (chunk.get()) actual_type{std::forward<Args>(args)...};
-            new (&storage) chunk_type *{chunk.get()};
-            chunk.release();
-
-            destroy_fn = &destroy_object<actual_type>;
-            copy_fn = &copy_object<actual_type>;
-            steal_fn = &steal_object<actual_type>;
-        }
+        node = internal::meta_info<Type>::resolve();
+        instance = traits_type::instance(storage, std::forward<Args>(args)...);
+        destroy_fn = &traits_type::destroy;
+        copy_fn = &traits_type::copy;
+        steal_fn = &traits_type::steal;
+        compare_fn = &traits_type::compare;
     }
 
     /**
@@ -622,7 +642,7 @@ public:
      * @return False if the container is empty, true otherwise.
      */
     explicit operator bool() const ENTT_NOEXCEPT {
-        return instance;
+        return destroy_fn;
     }
 
     /**
@@ -632,7 +652,7 @@ public:
      * otherwise.
      */
     bool operator==(const meta_any &other) const ENTT_NOEXCEPT {
-        return (!instance && !other.instance) || (instance && other.instance && node == other.node && compare_fn(instance, other.instance));
+        return (!compare_fn && !other.compare_fn) || (compare_fn && other.compare_fn && node == other.node && compare_fn(instance, other.instance));
     }
 
     /**

+ 131 - 0
test/entt/meta/meta.cpp

@@ -425,6 +425,61 @@ TEST_F(Meta, MetaAnyNoSBOMoveAssignment) {
     ASSERT_NE(other, fat_type{});
 }
 
+TEST_F(Meta, MetaAnyVoidInPlaceConstruction) {
+    entt::meta_any any{std::in_place_type<void>};
+
+    ASSERT_TRUE(any);
+    ASSERT_TRUE(any.can_cast<void>());
+    ASSERT_FALSE(any.can_cast<char>());
+    ASSERT_EQ(any.data(), nullptr);
+    ASSERT_EQ(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any, entt::meta_any{std::in_place_type<void>});
+}
+
+TEST_F(Meta, MetaAnyVoidCopyConstruction) {
+    entt::meta_any any{std::in_place_type<void>};
+    entt::meta_any other{any};
+
+    ASSERT_TRUE(any);
+    ASSERT_TRUE(other);
+    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
+}
+
+TEST_F(Meta, MetaAnyVoidCopyAssignment) {
+    entt::meta_any any{std::in_place_type<void>};
+    entt::meta_any other{std::in_place_type<void>};
+
+    other = any;
+
+    ASSERT_TRUE(any);
+    ASSERT_TRUE(other);
+    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
+}
+
+TEST_F(Meta, MetaAnyVoidMoveConstruction) {
+    entt::meta_any any{std::in_place_type<void>};
+    entt::meta_any other{std::move(any)};
+
+    ASSERT_FALSE(any);
+    ASSERT_TRUE(other);
+    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
+}
+
+TEST_F(Meta, MetaAnyVoidMoveAssignment) {
+    entt::meta_any any{std::in_place_type<void>};
+    entt::meta_any other{std::in_place_type<void>};
+
+    other = std::move(any);
+
+    ASSERT_FALSE(any);
+    ASSERT_TRUE(other);
+    ASSERT_TRUE(other.can_cast<void>());
+    ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
+}
+
 TEST_F(Meta, MetaAnySBOMoveInvalidate) {
     entt::meta_any any{42};
     entt::meta_any other{std::move(any)};
@@ -447,6 +502,16 @@ TEST_F(Meta, MetaAnyNoSBOMoveInvalidate) {
     ASSERT_TRUE(valid);
 }
 
+TEST_F(Meta, MetaAnyVoidMoveInvalidate) {
+    entt::meta_any any{std::in_place_type<void>};
+    entt::meta_any other{std::move(any)};
+    entt::meta_any valid = std::move(other);
+
+    ASSERT_FALSE(any);
+    ASSERT_FALSE(other);
+    ASSERT_TRUE(valid);
+}
+
 TEST_F(Meta, MetaAnySBODestruction) {
     ASSERT_EQ(empty_type::counter, 0);
     { entt::meta_any any{empty_type{}}; }
@@ -459,6 +524,11 @@ TEST_F(Meta, MetaAnyNoSBODestruction) {
     ASSERT_EQ(fat_type::counter, 1);
 }
 
+TEST_F(Meta, MetaAnyVoidDestruction) {
+    // just let asan tell us if everything is ok here
+    [[maybe_unused]] entt::meta_any any{std::in_place_type<void>};
+}
+
 TEST_F(Meta, MetaAnyEmplace) {
     entt::meta_any any{};
     any.emplace<int>(42);
@@ -475,6 +545,17 @@ TEST_F(Meta, MetaAnyEmplace) {
     ASSERT_NE(any, entt::meta_any{3});
 }
 
+TEST_F(Meta, MetaAnyEmplaceVoid) {
+    entt::meta_any any{};
+    any.emplace<void>();
+
+    ASSERT_TRUE(any);
+    ASSERT_TRUE(any.can_cast<void>());
+    ASSERT_EQ(any.data(), nullptr);
+    ASSERT_EQ(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any, (entt::meta_any{std::in_place_type<void>}));
+}
+
 TEST_F(Meta, MetaAnySBOSwap) {
     entt::meta_any lhs{'c'};
     entt::meta_any rhs{42};
@@ -498,6 +579,16 @@ TEST_F(Meta, MetaAnyNoSBOSwap) {
     ASSERT_EQ(rhs.cast<fat_type>().bar, &i);
 }
 
+TEST_F(Meta, MetaAnyVoidSwap) {
+    entt::meta_any lhs{std::in_place_type<void>};
+    entt::meta_any rhs{std::in_place_type<void>};
+    const auto *pre = lhs.data();
+
+    std::swap(lhs, rhs);
+
+    ASSERT_EQ(pre, lhs.data());
+}
+
 TEST_F(Meta, MetaAnySBOWithNoSBOSwap) {
     int value = 42;
     entt::meta_any lhs{fat_type{&value}};
@@ -529,6 +620,17 @@ TEST_F(Meta, MetaAnySBOWithEmptySwap) {
     ASSERT_EQ(lhs.cast<char>(), 'c');
 }
 
+TEST_F(Meta, MetaAnySBOWithVoidSwap) {
+    entt::meta_any lhs{'c'};
+    entt::meta_any rhs{std::in_place_type<void>};
+
+    std::swap(lhs, rhs);
+
+    ASSERT_TRUE(lhs.can_cast<void>());
+    ASSERT_TRUE(rhs.can_cast<char>());
+    ASSERT_EQ(rhs.cast<char>(), 'c');
+}
+
 TEST_F(Meta, MetaAnyNoSBOWithEmptySwap) {
     int i;
     entt::meta_any lhs{fat_type{&i}};
@@ -543,6 +645,20 @@ TEST_F(Meta, MetaAnyNoSBOWithEmptySwap) {
     ASSERT_EQ(lhs.cast<fat_type>().bar, &i);
 }
 
+TEST_F(Meta, MetaAnyNoSBOWithVoidSwap) {
+    int i;
+    entt::meta_any lhs{fat_type{&i}};
+    entt::meta_any rhs{std::in_place_type<void>};
+
+    std::swap(lhs, rhs);
+
+    ASSERT_EQ(rhs.cast<fat_type>().bar, &i);
+
+    std::swap(lhs, rhs);
+
+    ASSERT_EQ(lhs.cast<fat_type>().bar, &i);
+}
+
 TEST_F(Meta, MetaAnyComparable) {
     entt::meta_any any{'c'};
 
@@ -570,6 +686,21 @@ TEST_F(Meta, MetaAnyNotComparable) {
     ASSERT_TRUE(any != entt::meta_any{});
 }
 
+TEST_F(Meta, MetaAnyCompareVoid) {
+    entt::meta_any any{std::in_place_type<void>};
+
+    ASSERT_EQ(any, any);
+    ASSERT_EQ(any, entt::meta_any{std::in_place_type<void>});
+    ASSERT_NE(any, entt::meta_any{'a'});
+    ASSERT_NE(any, entt::meta_any{});
+
+    ASSERT_TRUE(any == any);
+    ASSERT_TRUE(any == entt::meta_any{std::in_place_type<void>});
+    ASSERT_FALSE(any == entt::meta_any{'a'});
+    ASSERT_TRUE(any != entt::meta_any{'a'});
+    ASSERT_TRUE(any != entt::meta_any{});
+}
+
 TEST_F(Meta, MetaAnyCast) {
     entt::meta_any any{derived_type{}};
     entt::meta_handle handle{any};