diff --git a/docs/md/core.md b/docs/md/core.md
index 58ca50cb7..de1f70ad7 100644
--- a/docs/md/core.md
+++ b/docs/md/core.md
@@ -262,11 +262,26 @@ element when required, regardless of the storage strategy used for the specific
object.
Furthermore, an instance of `any` is not tied to an actual type. Therefore, the
wrapper will be reconfigured by assigning it an object of a different type than
-the one contained, so as to be able to handle the new instance.
+the one contained, so as to be able to handle the new instance.
+
+There exists also a way to directly assign a value to the variable contained by
+an `entt::any`, without necessarily replacing it. This is especially useful when
+the object is used in _aliasing mode_, as described below:
+
+```cpp
+entt::any any{42};
+any.assign(3);
+```
+
+The `any` class will also perform a check on the type information and whether or
+not the original type was copy or move assignable, as appropriate.
+In all cases, the `assign` function returns a boolean value to indicate the
+success or failure of the operation.
+
When in doubt about the type of object contained, the `type` member function of
-`any` returns an instance of `type_info` associated with its element, or an
-invalid `type_info` object if the container is empty. The type is also used
-internally when comparing two `any` objects:
+`any` returns a const reference to the `type_info` associated with its element,
+or `type_id()` if the container is empty. The type is also used internally
+when comparing two `any` objects:
```cpp
if(any == empty) { /* ... */ }
@@ -274,7 +289,7 @@ if(any == empty) { /* ... */ }
In this case, before proceeding with a comparison, it's verified that the _type_
of the two objects is actually the same.
-Refer to the `EnTT` type system documentation for more details on how
+Refer to the `EnTT` type system documentation for more details about how
`type_info` works and on possible risks of a comparison.
A particularly interesting feature of this class is that it can also be used as
@@ -382,7 +397,7 @@ information to that provided by its counterpart.
Basically, the whole system relies on a handful of classes. In particular:
-* The unique, sequential identifier associated with a given type:
+* The unique sequential identifier associated with a given type:
```cpp
auto index = entt::type_index::value();
@@ -483,13 +498,13 @@ Therefore, they can sometimes be even more reliable than those obtained
otherwise.
A type info object is an opaque class that is also copy and move constructible.
-This class is returned by the `type_id` function template:
+Objects of this class are returned by the `type_id` function template:
```cpp
auto info = entt::type_id();
```
-These are the information made available by this object:
+These are the information made available by a `type_info` object:
* The index associated with a given type:
diff --git a/src/entt/core/any.hpp b/src/entt/core/any.hpp
index 51ddafe25..ff5d07489 100644
--- a/src/entt/core/any.hpp
+++ b/src/entt/core/any.hpp
@@ -23,8 +23,10 @@ class basic_any {
enum class operation : std::uint8_t {
copy,
move,
- dtor,
- comp,
+ assign,
+ transfer,
+ destroy,
+ compare,
get
};
@@ -65,7 +67,17 @@ class basic_any {
}
return (static_cast(const_cast(to))->instance = std::exchange(const_cast(from).instance, nullptr));
- case operation::dtor:
+ case operation::assign:
+ if constexpr(std::is_copy_assignable_v) {
+ return std::addressof(*const_cast(instance) = *static_cast(to));
+ }
+ break;
+ case operation::transfer:
+ if constexpr(std::is_move_assignable_v) {
+ return std::addressof(*const_cast(instance) = std::move(*static_cast(const_cast(to))));
+ }
+ break;
+ case operation::destroy:
if constexpr(in_situ) {
instance->~Type();
} else if constexpr(std::is_array_v) {
@@ -74,9 +86,9 @@ class basic_any {
delete instance;
}
break;
- case operation::comp:
+ case operation::compare:
if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) {
- return to && (*static_cast(instance) == *static_cast(to)) ? to : nullptr;
+ return *static_cast(instance) == *static_cast(to) ? to : nullptr;
} else {
return (instance == to) ? to : nullptr;
}
@@ -183,7 +195,7 @@ public:
/*! @brief Frees the internal storage, whatever it means. */
~basic_any() {
if(vtable && mode == policy::owner) {
- vtable(operation::dtor, *this, nullptr);
+ vtable(operation::destroy, *this, nullptr);
}
}
@@ -287,10 +299,40 @@ public:
initialize(std::forward(args)...);
}
+ /**
+ * @brief Copy assigns a value to the contained object without replacing it.
+ * @param other The value to assign to the contained object.
+ * @return True in case of success, false otherwise.
+ */
+ bool assign(const any &other) {
+ if(vtable && mode != policy::cref && *info == *other.info) {
+ return (vtable(operation::assign, *this, other.data()) != nullptr);
+ }
+
+ return false;
+ }
+
+ /**
+ * @brief Move assigns a value to the contained object without replacing it.
+ * @param other The value to assign to the contained object.
+ * @return True in case of success, false otherwise.
+ */
+ bool assign(any &&other) {
+ if(vtable && mode != policy::cref && *info == *other.info) {
+ if(auto *val = other.data(); val) {
+ return (vtable(operation::transfer, *this, val) != nullptr);
+ } else {
+ return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
+ }
+ }
+
+ return false;
+ }
+
/*! @brief Destroys contained object */
void reset() {
if(vtable && mode == policy::owner) {
- vtable(operation::dtor, *this, nullptr);
+ vtable(operation::destroy, *this, nullptr);
}
info = &type_id();
@@ -313,7 +355,7 @@ public:
*/
bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
if(vtable && *info == *other.info) {
- return (vtable(operation::comp, *this, other.data()) != nullptr);
+ return (vtable(operation::compare, *this, other.data()) != nullptr);
}
return (!vtable && !other.vtable);
diff --git a/test/entt/core/any.cpp b/test/entt/core/any.cpp
index ca58ef08d..1579294e8 100644
--- a/test/entt/core/any.cpp
+++ b/test/entt/core/any.cpp
@@ -34,7 +34,6 @@ struct not_comparable {
bool operator==(const not_comparable &) const = delete;
};
-template
struct not_copyable {
not_copyable()
: payload{} {}
@@ -45,7 +44,7 @@ struct not_copyable {
not_copyable &operator=(const not_copyable &) = delete;
not_copyable &operator=(not_copyable &&) = default;
- double payload[Sz];
+ double payload;
};
struct alignas(64u) over_aligned {};
@@ -251,6 +250,97 @@ TEST_F(Any, SBODirectAssignment) {
ASSERT_EQ(entt::any_cast(any), 42);
}
+TEST_F(Any, SBOAssignValue) {
+ entt::any any{42};
+ entt::any other{3};
+ entt::any invalid{'c'};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), 42);
+
+ ASSERT_TRUE(any.assign(other));
+ ASSERT_FALSE(any.assign(invalid));
+ ASSERT_EQ(entt::any_cast(any), 3);
+}
+
+TEST_F(Any, SBOAsRefAssignValue) {
+ int value = 42;
+ entt::any any{entt::forward_as_any(value)};
+ entt::any other{3};
+ entt::any invalid{'c'};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), 42);
+
+ ASSERT_TRUE(any.assign(other));
+ ASSERT_FALSE(any.assign(invalid));
+ ASSERT_EQ(entt::any_cast(any), 3);
+ ASSERT_EQ(value, 3);
+}
+
+TEST_F(Any, SBOAsConstRefAssignValue) {
+ const int value = 42;
+ entt::any any{entt::forward_as_any(value)};
+ entt::any other{3};
+ entt::any invalid{'c'};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), 42);
+
+ ASSERT_FALSE(any.assign(other));
+ ASSERT_FALSE(any.assign(invalid));
+ ASSERT_EQ(entt::any_cast(any), 42);
+ ASSERT_EQ(value, 42);
+}
+
+TEST_F(Any, SBOTransferValue) {
+ entt::any any{42};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), 42);
+
+ ASSERT_TRUE(any.assign(3));
+ ASSERT_FALSE(any.assign('c'));
+ ASSERT_EQ(entt::any_cast(any), 3);
+}
+
+TEST_F(Any, SBOTransferConstValue) {
+ const int value = 3;
+ entt::any any{42};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), 42);
+
+ ASSERT_TRUE(any.assign(entt::forward_as_any(value)));
+ ASSERT_EQ(entt::any_cast(any), 3);
+}
+
+TEST_F(Any, SBOAsRefTransferValue) {
+ int value = 42;
+ entt::any any{entt::forward_as_any(value)};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), 42);
+
+ ASSERT_TRUE(any.assign(3));
+ ASSERT_FALSE(any.assign('c'));
+ ASSERT_EQ(entt::any_cast(any), 3);
+ ASSERT_EQ(value, 3);
+}
+
+TEST_F(Any, SBOAsConstRefTransferValue) {
+ const int value = 42;
+ entt::any any{entt::forward_as_any(value)};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), 42);
+
+ ASSERT_FALSE(any.assign(3));
+ ASSERT_FALSE(any.assign('c'));
+ ASSERT_EQ(entt::any_cast(any), 42);
+ ASSERT_EQ(value, 42);
+}
+
TEST_F(Any, NoSBOInPlaceTypeConstruction) {
fat instance{.1, .2, .3, .4};
entt::any any{std::in_place_type, instance};
@@ -420,6 +510,112 @@ TEST_F(Any, NoSBODirectAssignment) {
ASSERT_EQ(entt::any_cast(any), instance);
}
+TEST_F(Any, NoSBOAssignValue) {
+ entt::any any{fat{.1, .2, .3, .4}};
+ entt::any other{fat{.0, .1, .2, .3}};
+ entt::any invalid{'c'};
+
+ const void *addr = std::as_const(any).data();
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+
+ ASSERT_TRUE(any.assign(other));
+ ASSERT_FALSE(any.assign(invalid));
+ ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3}));
+ ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(Any, NoSBOAsRefAssignValue) {
+ fat instance{.1, .2, .3, .4};
+ entt::any any{entt::forward_as_any(instance)};
+ entt::any other{fat{.0, .1, .2, .3}};
+ entt::any invalid{'c'};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+
+ ASSERT_TRUE(any.assign(other));
+ ASSERT_FALSE(any.assign(invalid));
+ ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3}));
+ ASSERT_EQ(instance, (fat{.0, .1, .2, .3}));
+}
+
+TEST_F(Any, NoSBOAsConstRefAssignValue) {
+ const fat instance{.1, .2, .3, .4};
+ entt::any any{entt::forward_as_any(instance)};
+ entt::any other{fat{.0, .1, .2, .3}};
+ entt::any invalid{'c'};
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+
+ ASSERT_FALSE(any.assign(other));
+ ASSERT_FALSE(any.assign(invalid));
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+ ASSERT_EQ(instance, (fat{.1, .2, .3, .4}));
+}
+
+TEST_F(Any, NoSBOTransferValue) {
+ entt::any any{fat{.1, .2, .3, .4}};
+
+ const void *addr = std::as_const(any).data();
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+
+ ASSERT_TRUE(any.assign(fat{.0, .1, .2, .3}));
+ ASSERT_FALSE(any.assign('c'));
+ ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3}));
+ ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(Any, NoSBOTransferConstValue) {
+ const fat instance{.0, .1, .2, .3};
+ entt::any any{fat{.1, .2, .3, .4}};
+
+ const void *addr = std::as_const(any).data();
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+
+ ASSERT_TRUE(any.assign(entt::forward_as_any(instance)));
+ ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3}));
+ ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(Any, NoSBOAsRefTransferValue) {
+ fat instance{.1, .2, .3, .4};
+ entt::any any{entt::forward_as_any(instance)};
+
+ const void *addr = std::as_const(any).data();
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+
+ ASSERT_TRUE(any.assign(fat{.0, .1, .2, .3}));
+ ASSERT_FALSE(any.assign('c'));
+ ASSERT_EQ(entt::any_cast(any), (fat{.0, .1, .2, .3}));
+ ASSERT_EQ(instance, (fat{.0, .1, .2, .3}));
+ ASSERT_EQ(addr, std::as_const(any).data());
+}
+
+TEST_F(Any, NoSBOAsConstRefTransferValue) {
+ const fat instance{.1, .2, .3, .4};
+ entt::any any{entt::forward_as_any(instance)};
+
+ const void *addr = std::as_const(any).data();
+
+ ASSERT_TRUE(any);
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+
+ ASSERT_FALSE(any.assign(fat{.0, .1, .2, .3}));
+ ASSERT_FALSE(any.assign('c'));
+ ASSERT_EQ(entt::any_cast(any), (fat{.1, .2, .3, .4}));
+ ASSERT_EQ(instance, (fat{.1, .2, .3, .4}));
+ ASSERT_EQ(addr, std::as_const(any).data());
+}
+
TEST_F(Any, VoidInPlaceTypeConstruction) {
entt::any any{std::in_place_type};
@@ -971,13 +1167,13 @@ TEST_F(Any, AnyCast) {
ASSERT_EQ(entt::any_cast(cany), 42);
ASSERT_DEATH(entt::any_cast(cany), "");
- not_copyable<1> instance{};
- instance.payload[0u] = 42.;
+ not_copyable instance{};
+ instance.payload = 42.;
entt::any ref{entt::forward_as_any(instance)};
- entt::any cref{entt::forward_as_any(std::as_const(instance).payload[0u])};
+ entt::any cref{entt::forward_as_any(std::as_const(instance).payload)};
- ASSERT_EQ(entt::any_cast>(std::move(ref)).payload[0u], 42.);
- ASSERT_DEATH(entt::any_cast>(std::as_const(ref).as_ref()), "");
+ ASSERT_EQ(entt::any_cast(std::move(ref)).payload, 42.);
+ ASSERT_DEATH(entt::any_cast(std::as_const(ref).as_ref()), "");
ASSERT_EQ(entt::any_cast(std::move(cref)), 42.);
ASSERT_DEATH(entt::any_cast(entt::any{42}), "");
ASSERT_EQ(entt::any_cast(entt::any{42}), 42);
@@ -1037,7 +1233,17 @@ TEST_F(Any, ForwardAsAny) {
}
TEST_F(Any, NotCopyableType) {
- auto test = [](entt::any any) {
+ auto test = [](entt::any any, entt::any other) {
+ ASSERT_TRUE(any);
+ ASSERT_TRUE(other);
+
+ ASSERT_TRUE(any.owner());
+ ASSERT_FALSE(other.owner());
+ ASSERT_EQ(any.type(), other.type());
+
+ ASSERT_FALSE(any.assign(other));
+ ASSERT_FALSE(any.assign(std::move(other)));
+
entt::any copy{any};
ASSERT_TRUE(any);
@@ -1055,8 +1261,8 @@ TEST_F(Any, NotCopyableType) {
ASSERT_TRUE(copy.owner());
};
- test(entt::any{std::in_place_type>});
- test(entt::any{std::in_place_type>});
+ const not_copyable value;
+ test(entt::any{std::in_place_type}, entt::forward_as_any(value));
}
TEST_F(Any, Array) {