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};