Răsfoiți Sursa

any: support for non-copyable types

Michele Caini 5 ani în urmă
părinte
comite
35276b55af
2 a modificat fișierele cu 53 adăugiri și 29 ștergeri
  1. 39 27
      src/entt/core/any.hpp
  2. 14 2
      test/entt/core/any.cpp

+ 39 - 27
src/entt/core/any.hpp

@@ -33,12 +33,9 @@ class any {
         }
     }
 
-    static type_info & as_type_info(const void *data) {
-        return *const_cast<type_info *>(static_cast<const type_info *>(data));
-    }
-
-    static any & as_any(const void *data) {
-        return *const_cast<any *>(static_cast<const any *>(data));
+    template<typename Type>
+    static Type & as(const void *to) {
+        return *const_cast<Type *>(static_cast<const Type *>(to));
     }
 
     template<typename Type>
@@ -48,14 +45,9 @@ class any {
                 using base_type = std::remove_reference_t<Type>;
 
                 switch(op) {
-                case operation::REF:
-                case operation::CREF:
-                    as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type> : basic_vtable<const base_type &>;
-                    [[fallthrough]];
                 case operation::COPY:
                 case operation::MOVE:
-                    as_any(to).instance = from.instance;
-                    [[fallthrough]];
+                    return (as<any>(to).instance = from.instance);
                 case operation::DTOR:
                     break;
                 case operation::COMP:
@@ -64,8 +56,16 @@ class any {
                     return std::is_const_v<base_type> ? nullptr : from.instance;
                 case operation::CADDR:
                     return from.instance;
+                case operation::REF:
+                    as<any>(to).vtable = basic_vtable<Type>;
+                    as<any>(to).instance = from.instance;
+                    break;
+                case operation::CREF:
+                    as<any>(to).vtable = basic_vtable<const base_type &>;
+                    as<any>(to).instance = from.instance;
+                    break;
                 case operation::TYPE:
-                    as_type_info(to) = type_id<std::remove_const_t<base_type>>();
+                    as<type_info>(to) = type_id<std::remove_const_t<base_type>>();
                     break;
                 }
             } else if constexpr(in_situ<Type>) {
@@ -77,10 +77,12 @@ class any {
 
                 switch(op) {
                 case operation::COPY:
-                    new (&as_any(to).storage) Type{std::as_const(*instance)};
+                    if constexpr(std::is_copy_constructible_v<Type>) {
+                        return new (&as<any>(to).storage) Type{std::as_const(*instance)};
+                    }
                     break;
                 case operation::MOVE:
-                    new (&as_any(to).storage) Type{std::move(*instance)};
+                    new (&as<any>(to).storage) Type{std::move(*instance)};
                     [[fallthrough]];
                 case operation::DTOR:
                     instance->~Type();
@@ -91,25 +93,26 @@ class any {
                 case operation::CADDR:
                     return instance;
                 case operation::REF:
+                    as<any>(to).vtable = basic_vtable<Type &>;
+                    as<any>(to).instance = instance;
+                    break;
                 case operation::CREF:
-                    as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type &> : basic_vtable<const Type &>;
-                    as_any(to).instance = instance;
+                    as<any>(to).vtable = basic_vtable<const Type &>;
+                    as<any>(to).instance = instance;
                     break;
                 case operation::TYPE:
-                    as_type_info(to) = type_id<Type>();
+                    as<type_info>(to) = type_id<Type>();
                     break;
                 }
             } else {
                 switch(op) {
                 case operation::COPY:
-                    as_any(to).instance = new Type{*static_cast<const Type *>(from.instance)};
+                    if constexpr(std::is_copy_constructible_v<Type>) {
+                        return (as<any>(to).instance = new Type{*static_cast<const Type *>(from.instance)});
+                    }
                     break;
-                case operation::REF:
-                case operation::CREF:
-                    as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type &> : basic_vtable<const Type &>;
-                    [[fallthrough]];
                 case operation::MOVE:
-                    as_any(to).instance = from.instance;
+                    as<any>(to).instance = from.instance;
                     break;
                 case operation::DTOR:
                     delete static_cast<const Type *>(from.instance);
@@ -119,8 +122,16 @@ class any {
                 case operation::ADDR:
                 case operation::CADDR:
                     return from.instance;
+                case operation::REF:
+                    as<any>(to).vtable = basic_vtable<Type &>;
+                    as<any>(to).instance = from.instance;
+                    break;
+                case operation::CREF:
+                    as<any>(to).vtable = basic_vtable<const Type &>;
+                    as<any>(to).instance = from.instance;
+                    break;
                 case operation::TYPE:
-                    as_type_info(to) = type_id<Type>();
+                    as<type_info>(to) = type_id<Type>();
                     break;
                 }
             }
@@ -186,8 +197,9 @@ public:
     any(const any &other)
         : any{}
     {
-        vtable = other.vtable;
-        vtable(operation::COPY, other, this);
+        if(other.vtable(operation::COPY, other, this)) {
+            vtable = other.vtable;
+        }
     }
 
     /**

+ 14 - 2
test/entt/core/any.cpp

@@ -1,4 +1,5 @@
 #include <algorithm>
+#include <memory>
 #include <gtest/gtest.h>
 #include <entt/core/any.hpp>
 
@@ -288,7 +289,6 @@ TEST(Any, NoSBOMoveConstruction) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(any.type());
     ASSERT_EQ(other.type(), entt::type_id<fat>());
     ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
     ASSERT_EQ(entt::any_cast<fat>(other), instance);
@@ -303,7 +303,6 @@ TEST(Any, NoSBOMoveAssignment) {
 
     ASSERT_FALSE(any);
     ASSERT_TRUE(other);
-    ASSERT_FALSE(any.type());
     ASSERT_EQ(other.type(), entt::type_id<fat>());
     ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
     ASSERT_EQ(entt::any_cast<fat>(other), instance);
@@ -775,3 +774,16 @@ TEST(Any, AnyCast) {
     ASSERT_EQ(entt::any_cast<const int &>(cany), 42);
     ASSERT_EQ(entt::any_cast<int>(42), 42);
 }
+
+TEST(Any, NonCopyableType) {
+    entt::any any{std::in_place_type<std::unique_ptr<int>>, new int{42}};
+    entt::any copy{any};
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(copy);
+
+    copy = any;
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(copy);
+}