any: added the ::assign function to copy/move assign the wrapped variable
This commit is contained in:
@@ -262,11 +262,26 @@ element when required, regardless of the storage strategy used for the specific
|
||||
object.<br/>
|
||||
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.<br/>
|
||||
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.<br/>
|
||||
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<void>()` 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.<br/>
|
||||
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<a_type>::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<a_type>();
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
|
||||
@@ -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<basic_any *>(const_cast<void *>(to))->instance = std::exchange(const_cast<basic_any &>(from).instance, nullptr));
|
||||
case operation::dtor:
|
||||
case operation::assign:
|
||||
if constexpr(std::is_copy_assignable_v<Type>) {
|
||||
return std::addressof(*const_cast<Type *>(instance) = *static_cast<const Type *>(to));
|
||||
}
|
||||
break;
|
||||
case operation::transfer:
|
||||
if constexpr(std::is_move_assignable_v<Type>) {
|
||||
return std::addressof(*const_cast<Type *>(instance) = std::move(*static_cast<Type *>(const_cast<void *>(to))));
|
||||
}
|
||||
break;
|
||||
case operation::destroy:
|
||||
if constexpr(in_situ<Type>) {
|
||||
instance->~Type();
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
@@ -74,9 +86,9 @@ class basic_any {
|
||||
delete instance;
|
||||
}
|
||||
break;
|
||||
case operation::comp:
|
||||
case operation::compare:
|
||||
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return to && (*static_cast<const Type *>(instance) == *static_cast<const Type *>(to)) ? to : nullptr;
|
||||
return *static_cast<const Type *>(instance) == *static_cast<const Type *>(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<Type>(std::forward<Args>(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<void>();
|
||||
@@ -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);
|
||||
|
||||
@@ -34,7 +34,6 @@ struct not_comparable {
|
||||
bool operator==(const not_comparable &) const = delete;
|
||||
};
|
||||
|
||||
template<auto Sz>
|
||||
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<int>(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<int>(any), 42);
|
||||
|
||||
ASSERT_TRUE(any.assign(other));
|
||||
ASSERT_FALSE(any.assign(invalid));
|
||||
ASSERT_EQ(entt::any_cast<int>(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<int>(any), 42);
|
||||
|
||||
ASSERT_TRUE(any.assign(other));
|
||||
ASSERT_FALSE(any.assign(invalid));
|
||||
ASSERT_EQ(entt::any_cast<int>(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<int>(any), 42);
|
||||
|
||||
ASSERT_FALSE(any.assign(other));
|
||||
ASSERT_FALSE(any.assign(invalid));
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
ASSERT_EQ(value, 42);
|
||||
}
|
||||
|
||||
TEST_F(Any, SBOTransferValue) {
|
||||
entt::any any{42};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
|
||||
ASSERT_TRUE(any.assign(3));
|
||||
ASSERT_FALSE(any.assign('c'));
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 3);
|
||||
}
|
||||
|
||||
TEST_F(Any, SBOTransferConstValue) {
|
||||
const int value = 3;
|
||||
entt::any any{42};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
|
||||
ASSERT_TRUE(any.assign(entt::forward_as_any(value)));
|
||||
ASSERT_EQ(entt::any_cast<int>(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<int>(any), 42);
|
||||
|
||||
ASSERT_TRUE(any.assign(3));
|
||||
ASSERT_FALSE(any.assign('c'));
|
||||
ASSERT_EQ(entt::any_cast<int>(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<int>(any), 42);
|
||||
|
||||
ASSERT_FALSE(any.assign(3));
|
||||
ASSERT_FALSE(any.assign('c'));
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
ASSERT_EQ(value, 42);
|
||||
}
|
||||
|
||||
TEST_F(Any, NoSBOInPlaceTypeConstruction) {
|
||||
fat instance{.1, .2, .3, .4};
|
||||
entt::any any{std::in_place_type<fat>, instance};
|
||||
@@ -420,6 +510,112 @@ TEST_F(Any, NoSBODirectAssignment) {
|
||||
ASSERT_EQ(entt::any_cast<fat>(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<const fat &>(any), (fat{.1, .2, .3, .4}));
|
||||
|
||||
ASSERT_TRUE(any.assign(other));
|
||||
ASSERT_FALSE(any.assign(invalid));
|
||||
ASSERT_EQ(entt::any_cast<const fat &>(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<const fat &>(any), (fat{.1, .2, .3, .4}));
|
||||
|
||||
ASSERT_TRUE(any.assign(other));
|
||||
ASSERT_FALSE(any.assign(invalid));
|
||||
ASSERT_EQ(entt::any_cast<const fat &>(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<const fat &>(any), (fat{.1, .2, .3, .4}));
|
||||
|
||||
ASSERT_FALSE(any.assign(other));
|
||||
ASSERT_FALSE(any.assign(invalid));
|
||||
ASSERT_EQ(entt::any_cast<const fat &>(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<const fat &>(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<const fat &>(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<const fat &>(any), (fat{.1, .2, .3, .4}));
|
||||
|
||||
ASSERT_TRUE(any.assign(entt::forward_as_any(instance)));
|
||||
ASSERT_EQ(entt::any_cast<const fat &>(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<const fat &>(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<const fat &>(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<const fat &>(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<const fat &>(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<void>};
|
||||
|
||||
@@ -971,13 +1167,13 @@ TEST_F(Any, AnyCast) {
|
||||
ASSERT_EQ(entt::any_cast<const int &>(cany), 42);
|
||||
ASSERT_DEATH(entt::any_cast<const double &>(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<not_copyable<1>>(std::move(ref)).payload[0u], 42.);
|
||||
ASSERT_DEATH(entt::any_cast<not_copyable<1>>(std::as_const(ref).as_ref()), "");
|
||||
ASSERT_EQ(entt::any_cast<not_copyable>(std::move(ref)).payload, 42.);
|
||||
ASSERT_DEATH(entt::any_cast<not_copyable>(std::as_const(ref).as_ref()), "");
|
||||
ASSERT_EQ(entt::any_cast<double>(std::move(cref)), 42.);
|
||||
ASSERT_DEATH(entt::any_cast<double>(entt::any{42}), "");
|
||||
ASSERT_EQ(entt::any_cast<int>(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<not_copyable<1>>});
|
||||
test(entt::any{std::in_place_type<not_copyable<4>>});
|
||||
const not_copyable value;
|
||||
test(entt::any{std::in_place_type<not_copyable>}, entt::forward_as_any(value));
|
||||
}
|
||||
|
||||
TEST_F(Any, Array) {
|
||||
|
||||
Reference in New Issue
Block a user