Przeglądaj źródła

meta any: in place construction/emplace

Michele Caini 6 lat temu
rodzic
commit
528b361e34
2 zmienionych plików z 91 dodań i 9 usunięć
  1. 43 9
      src/entt/meta/meta.hpp
  2. 48 0
      test/entt/meta/meta.cpp

+ 43 - 9
src/entt/meta/meta.hpp

@@ -365,7 +365,7 @@ public:
     {}
 
     /**
-     * @brief Constructs a meta any from a given value.
+     * @brief Constructs a meta any by directly initializing the new object.
      *
      * This class uses a technique called small buffer optimization (SBO) to
      * completely eliminate the need to allocate memory, where possible.<br/>
@@ -375,31 +375,52 @@ public:
      * increasing the overall performance.
      *
      * @tparam Type Type of object to use to initialize the container.
-     * @param type An instance of an object to use to initialize the container.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
      */
-    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>>>
-    meta_any(Type &&type) {
+    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>>;
+        constexpr auto sbo_allowed = sizeof(actual_type) <= sizeof(void *);
         node = internal::meta_info<Type>::resolve();
 
         compare_fn = &compare<actual_type>;
 
-        if constexpr(sizeof(actual_type) <= sizeof(void *)) {
-            instance = new (&storage) actual_type{std::forward<Type>(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>;
         } else {
             using chunk_type = std::aligned_storage_t<sizeof(actual_type), alignof(actual_type)>;
 
-            auto *chunk = new chunk_type;
-            instance = new (chunk) actual_type{std::forward<Type>(type)};
-            new (&storage) chunk_type *{chunk};
+            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>;
         }
     }
 
+    /**
+     * @brief Constructs a meta any from a given value.
+     *
+     * This class uses a technique called small buffer optimization (SBO) to
+     * completely eliminate the need to allocate memory, where possible.<br/>
+     * From the user's point of view, nothing will change, but the elimination
+     * of allocations will reduce the jumps in memory and therefore will avoid
+     * chasing of pointers. This will greatly improve the use of the cache, thus
+     * increasing the overall performance.
+     *
+     * @tparam Type Type of object to use to initialize the container.
+     * @param type An instance of an object to use to initialize the container.
+     */
+    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>>>
+    meta_any(Type &&type)
+        : meta_any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(type)}
+    {}
+
     /**
      * @brief Copy constructor.
      * @param other The instance to copy from.
@@ -562,6 +583,19 @@ public:
         return valid;
     }
 
+    /**
+     * @brief Replaces the contained object by initializing a new instance
+     * directly.
+     * @tparam Type Type of object to use to initialize the container.
+     * @tparam Args Types of arguments to use to construct the new instance.
+     * @param args Parameters to use to construct the instance.
+     */
+    template<typename Type, typename... Args>
+    void emplace(Args&& ... args) {
+        [[maybe_unused]] meta_any other{std::move(*this)};
+        *this = meta_any{std::in_place_type_t<Type>{}, std::forward<Args>(args)...};
+    }
+
     /**
      * @brief Returns false if a container is empty, true otherwise.
      * @return False if the container is empty, true otherwise.

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

@@ -264,6 +264,21 @@ TEST_F(Meta, MetaAnyEmpty) {
     ASSERT_NE(any, entt::meta_any{'c'});
 }
 
+TEST_F(Meta, MetaAnySBOInPlaceConstruction) {
+    entt::meta_any any{std::in_place_type<int>, 42};
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(any.can_cast<void>());
+    ASSERT_TRUE(any.can_cast<int>());
+    ASSERT_EQ(any.cast<int>(), 42);
+    ASSERT_EQ(std::as_const(any).cast<int>(), 42);
+    ASSERT_NE(any.data(), nullptr);
+    ASSERT_NE(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any, (entt::meta_any{std::in_place_type<int>, 42}));
+    ASSERT_EQ(any, entt::meta_any{42});
+    ASSERT_NE(any, entt::meta_any{3});
+}
+
 TEST_F(Meta, MetaAnySBOCopyConstruction) {
     entt::meta_any any{42};
     entt::meta_any other{any};
@@ -324,6 +339,23 @@ TEST_F(Meta, MetaAnySBOMoveAssignment) {
     ASSERT_NE(other, entt::meta_any{0});
 }
 
+TEST_F(Meta, MetaAnyNoSBOInPlaceConstruction) {
+    int value = 42;
+    fat_type instance{&value};
+    entt::meta_any any{std::in_place_type<fat_type>, instance};
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(any.can_cast<void>());
+    ASSERT_TRUE(any.can_cast<fat_type>());
+    ASSERT_EQ(any.cast<fat_type>(), instance);
+    ASSERT_EQ(std::as_const(any).cast<fat_type>(), instance);
+    ASSERT_NE(any.data(), nullptr);
+    ASSERT_NE(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any, (entt::meta_any{std::in_place_type<fat_type>, instance}));
+    ASSERT_EQ(any, entt::meta_any{instance});
+    ASSERT_NE(any, entt::meta_any{fat_type{}});
+}
+
 TEST_F(Meta, MetaAnyNoSBOCopyConstruction) {
     int value = 42;
     fat_type instance{&value};
@@ -404,6 +436,22 @@ TEST_F(Meta, MetaAnyNoSBODestruction) {
     ASSERT_EQ(fat_type::counter, 1);
 }
 
+TEST_F(Meta, MetaAnyEmplace) {
+    entt::meta_any any{};
+    any.emplace<int>(42);
+
+    ASSERT_TRUE(any);
+    ASSERT_FALSE(any.can_cast<void>());
+    ASSERT_TRUE(any.can_cast<int>());
+    ASSERT_EQ(any.cast<int>(), 42);
+    ASSERT_EQ(std::as_const(any).cast<int>(), 42);
+    ASSERT_NE(any.data(), nullptr);
+    ASSERT_NE(std::as_const(any).data(), nullptr);
+    ASSERT_EQ(any, (entt::meta_any{std::in_place_type<int>, 42}));
+    ASSERT_EQ(any, entt::meta_any{42});
+    ASSERT_NE(any, entt::meta_any{3});
+}
+
 TEST_F(Meta, MetaAnySBOSwap) {
     entt::meta_any lhs{'c'};
     entt::meta_any rhs{42};