From 528b361e3415346131aa87acd17b8dc577b150fa Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Sat, 8 Jun 2019 14:03:31 +0200 Subject: [PATCH] meta any: in place construction/emplace --- src/entt/meta/meta.hpp | 76 +++++++++++++++++++++++++++++------------ test/entt/meta/meta.cpp | 48 ++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 21 deletions(-) diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index 89bf04c1b..57f450add 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -364,6 +364,45 @@ public: copy_fn{nullptr} {} + /** + * @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.
+ * 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. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + meta_any(std::in_place_type_t, Args &&... args) { + using actual_type = std::remove_cv_t>; + constexpr auto sbo_allowed = sizeof(actual_type) <= sizeof(void *); + node = internal::meta_info::resolve(); + + compare_fn = &compare; + + if constexpr(sbo_allowed) { + instance = new (&storage) actual_type{std::forward(args)...}; + destroy_fn = &destroy_storage; + copy_fn = ©_storage; + } else { + using chunk_type = std::aligned_storage_t; + + auto chunk = std::make_unique(); + instance = new (chunk.get()) actual_type{std::forward(args)...}; + new (&storage) chunk_type *{chunk.get()}; + chunk.release(); + + destroy_fn = &destroy_object; + copy_fn = ©_object; + } + } + /** * @brief Constructs a meta any from a given value. * @@ -378,27 +417,9 @@ public: * @param type An instance of an object to use to initialize the container. */ template>, meta_any>>> - meta_any(Type &&type) { - using actual_type = std::remove_cv_t>; - node = internal::meta_info::resolve(); - - compare_fn = &compare; - - if constexpr(sizeof(actual_type) <= sizeof(void *)) { - instance = new (&storage) actual_type{std::forward(type)}; - destroy_fn = &destroy_storage; - copy_fn = ©_storage; - } else { - using chunk_type = std::aligned_storage_t; - - auto *chunk = new chunk_type; - instance = new (chunk) actual_type{std::forward(type)}; - new (&storage) chunk_type *{chunk}; - - destroy_fn = &destroy_object; - copy_fn = ©_object; - } - } + meta_any(Type &&type) + : meta_any{std::in_place_type>>, std::forward(type)} + {} /** * @brief Copy constructor. @@ -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 + void emplace(Args&& ... args) { + [[maybe_unused]] meta_any other{std::move(*this)}; + *this = meta_any{std::in_place_type_t{}, std::forward(args)...}; + } + /** * @brief Returns false if a container is empty, true otherwise. * @return False if the container is empty, true otherwise. diff --git a/test/entt/meta/meta.cpp b/test/entt/meta/meta.cpp index d25d7b487..56cecb4dc 100644 --- a/test/entt/meta/meta.cpp +++ b/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, 42}; + + ASSERT_TRUE(any); + ASSERT_FALSE(any.can_cast()); + ASSERT_TRUE(any.can_cast()); + ASSERT_EQ(any.cast(), 42); + ASSERT_EQ(std::as_const(any).cast(), 42); + ASSERT_NE(any.data(), nullptr); + ASSERT_NE(std::as_const(any).data(), nullptr); + ASSERT_EQ(any, (entt::meta_any{std::in_place_type, 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, instance}; + + ASSERT_TRUE(any); + ASSERT_FALSE(any.can_cast()); + ASSERT_TRUE(any.can_cast()); + ASSERT_EQ(any.cast(), instance); + ASSERT_EQ(std::as_const(any).cast(), instance); + ASSERT_NE(any.data(), nullptr); + ASSERT_NE(std::as_const(any).data(), nullptr); + ASSERT_EQ(any, (entt::meta_any{std::in_place_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(42); + + ASSERT_TRUE(any); + ASSERT_FALSE(any.can_cast()); + ASSERT_TRUE(any.can_cast()); + ASSERT_EQ(any.cast(), 42); + ASSERT_EQ(std::as_const(any).cast(), 42); + ASSERT_NE(any.data(), nullptr); + ASSERT_NE(std::as_const(any).data(), nullptr); + ASSERT_EQ(any, (entt::meta_any{std::in_place_type, 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};