meta any: in place construction/emplace

This commit is contained in:
Michele Caini
2019-06-08 14:03:31 +02:00
parent a77bb3d341
commit 528b361e34
2 changed files with 103 additions and 21 deletions
+55 -21
View File
@@ -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.<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.
* @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>
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(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 = 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.
*
@@ -378,27 +417,9 @@ public:
* @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) {
using actual_type = std::remove_cv_t<std::remove_reference_t<Type>>;
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)};
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};
destroy_fn = &destroy_object<actual_type>;
copy_fn = &copy_object<actual_type>;
}
}
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.
@@ -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
View File
@@ -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};