core: standalone any class
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
* [Wide characters](wide-characters)
|
||||
* [Conflicts](#conflicts)
|
||||
* [Monostate](#monostate)
|
||||
* [Any as in any type](#any-as-in-any-type)
|
||||
* [Type support](#type-support)
|
||||
* [Type info](#type-info)
|
||||
* [Almost unique identifiers](#almost-unique-identifiers)
|
||||
@@ -214,6 +215,78 @@ const bool b = entt::monostate<"mykey"_hs>{};
|
||||
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
|
||||
```
|
||||
|
||||
# Any as in any type
|
||||
|
||||
`EnTT` comes with its own `any` type. It may seem redundant considering that
|
||||
C++17 introduced `std::any`, but it is not (hopefully).<br/>
|
||||
In fact, the _type_ returned by an `std::any` is a const reference to an
|
||||
`std::type_info`, an implementation defined class that's not something everyone
|
||||
wants to see in a software. Furthermore, there is no way to connect it with the
|
||||
type system of the library and therefore with its integrated RTTI support.<br/>
|
||||
Note that this class is largely used internally by the library itself.
|
||||
|
||||
The API is very similar to that of its most famous counterpart, mainly because
|
||||
this class serves the same purpose of being an opaque container for any type of
|
||||
value.<br/>
|
||||
Instances of `any` also minimize the number of allocations by relying on a well
|
||||
known technique called _small buffer optimization_ and a fake vtable.
|
||||
|
||||
Creating an object of the `any` type, whether empty or not, is trivial:
|
||||
|
||||
```cpp
|
||||
// an empty container
|
||||
entt::any empty{};
|
||||
|
||||
// a container for an int
|
||||
entt::any any{0};
|
||||
|
||||
// in place construction
|
||||
entt::any in_place{std::in_place_type<int>, 42};
|
||||
```
|
||||
|
||||
The `any` class takes the burden of destroying the contained 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/>
|
||||
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.
|
||||
|
||||
A particularly interesting feature of this class is that it can also be used as
|
||||
an opaque container for non-const unmanaged elements:
|
||||
|
||||
```cpp
|
||||
int value;
|
||||
entt::any any{std::ref(value)};
|
||||
```
|
||||
|
||||
In other words, whenever `any` intercepts a `reference_wrapper`, it acts as a
|
||||
reference to the original instance rather than making a copy of or moving it
|
||||
internally. The contained object is never destroyed and users must ensure that
|
||||
its lifetime exceeds that of the container.<br/>
|
||||
Similarly, it's possible to create non-owning copies of `any` from an existing
|
||||
object:
|
||||
|
||||
```cpp
|
||||
// aliasing constructor
|
||||
entt::any ref = other.ref();
|
||||
```
|
||||
|
||||
In this case, it doesn't matter if the starting container actually holds an
|
||||
object or acts already as a reference for unmanaged elements, the new instance
|
||||
thus created won't create copies and will only serve as a reference for the
|
||||
original item.<br/>
|
||||
It means that, starting from the example above, both `ref` and` other` will
|
||||
point to the same object, whether it's initially contained in `other` or already
|
||||
an unmanaged element.
|
||||
|
||||
To cast an instance of `any` to a type, the library offers a set of `any_cast`
|
||||
functions in all respects similar to their most famous counterparts.<br/>
|
||||
The only difference is that, in the case of `EnTT`, these won't raise exceptions
|
||||
but will only cross an assert in debug mode, otherwise resulting in undefined
|
||||
behavior in case of misuse in release mode.
|
||||
|
||||
# Type support
|
||||
|
||||
`EnTT` provides some basic information about types of all kinds.<br/>
|
||||
|
||||
229
src/entt/core/any.hpp
Normal file
229
src/entt/core/any.hpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#ifndef ENTT_CORE_ANY_HPP
|
||||
#define ENTT_CORE_ANY_HPP
|
||||
|
||||
|
||||
#include <functional>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "type_info.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
class any {
|
||||
enum class operation { COPY, MOVE, DTOR, ADDR, REF, TYPE };
|
||||
|
||||
using storage_type = std::aligned_storage_t<sizeof(double[2]), alignof(double[2])>;
|
||||
using vtable_type = void *(const operation, const any &, void *);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr auto in_situ = sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
|
||||
|
||||
template<typename Type>
|
||||
static void * basic_vtable(const operation op, const any &from, void *to) {
|
||||
if constexpr(std::is_void_v<Type>) {
|
||||
return nullptr;
|
||||
} else if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
switch(op) {
|
||||
case operation::REF:
|
||||
static_cast<any *>(to)->vtable = from.vtable;
|
||||
[[fallthrough]];
|
||||
case operation::COPY:
|
||||
case operation::MOVE:
|
||||
static_cast<any *>(to)->instance = from.instance;
|
||||
[[fallthrough]];
|
||||
case operation::DTOR:
|
||||
break;
|
||||
case operation::ADDR:
|
||||
return from.instance;
|
||||
case operation::TYPE:
|
||||
*static_cast<type_info *>(to) = type_id<std::remove_reference_t<Type>>();
|
||||
break;
|
||||
}
|
||||
} else if constexpr(in_situ<Type>) {
|
||||
auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));
|
||||
|
||||
switch(op) {
|
||||
case operation::COPY:
|
||||
new (&static_cast<any *>(to)->storage) Type{std::as_const(*instance)};
|
||||
break;
|
||||
case operation::MOVE:
|
||||
new (&static_cast<any *>(to)->storage) Type{std::move(*instance)};
|
||||
[[fallthrough]];
|
||||
case operation::DTOR:
|
||||
instance->~Type();
|
||||
break;
|
||||
case operation::ADDR:
|
||||
return instance;
|
||||
case operation::REF:
|
||||
static_cast<any *>(to)->vtable = basic_vtable<std::add_lvalue_reference_t<Type>>;
|
||||
static_cast<any *>(to)->instance = instance;
|
||||
break;
|
||||
case operation::TYPE:
|
||||
*static_cast<type_info *>(to) = type_id<Type>();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(op) {
|
||||
case operation::COPY:
|
||||
static_cast<any *>(to)->instance = new Type{std::as_const(*static_cast<Type *>(from.instance))};
|
||||
break;
|
||||
case operation::MOVE:
|
||||
static_cast<any *>(to)->instance = from.instance;
|
||||
break;
|
||||
case operation::DTOR:
|
||||
delete static_cast<Type *>(from.instance);
|
||||
break;
|
||||
case operation::ADDR:
|
||||
return from.instance;
|
||||
case operation::REF:
|
||||
static_cast<any *>(to)->vtable = basic_vtable<std::add_lvalue_reference_t<Type>>;
|
||||
static_cast<any *>(to)->instance = from.instance;
|
||||
break;
|
||||
case operation::TYPE:
|
||||
*static_cast<type_info *>(to) = type_id<Type>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
any() ENTT_NOEXCEPT
|
||||
: vtable{&basic_vtable<void>},
|
||||
instance{}
|
||||
{}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
explicit any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
|
||||
: vtable{&basic_vtable<Type>},
|
||||
instance{}
|
||||
{
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
if constexpr(in_situ<Type>) {
|
||||
new (&storage) Type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
instance = new Type{std::forward<Args>(args)...};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
any(std::reference_wrapper<Type> value)
|
||||
: vtable{&basic_vtable<std::add_lvalue_reference_t<Type>>},
|
||||
instance{&value.get()}
|
||||
{}
|
||||
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, any>>>
|
||||
any(Type &&value)
|
||||
: any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
|
||||
{}
|
||||
|
||||
any(const any &other)
|
||||
: any{}
|
||||
{
|
||||
vtable = other.vtable;
|
||||
vtable(operation::COPY, other, this);
|
||||
}
|
||||
|
||||
any(any &&other) ENTT_NOEXCEPT
|
||||
: any{}
|
||||
{
|
||||
vtable = std::exchange(other.vtable, &basic_vtable<void>);
|
||||
vtable(operation::MOVE, other, this);
|
||||
}
|
||||
|
||||
~any() {
|
||||
vtable(operation::DTOR, *this, nullptr);
|
||||
}
|
||||
|
||||
any & operator=(any other) {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
|
||||
return vtable(operation::ADDR, *this, nullptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] void * data() ENTT_NOEXCEPT {
|
||||
return const_cast<void *>(std::as_const(*this).data());
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&... args) {
|
||||
*this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
[[nodiscard]] any ref() const ENTT_NOEXCEPT {
|
||||
any other{};
|
||||
vtable(operation::REF, *this, &other);
|
||||
return other;
|
||||
}
|
||||
|
||||
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
|
||||
type_info info;
|
||||
vtable(operation::TYPE, *this, &info);
|
||||
return info;
|
||||
}
|
||||
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return !(vtable(operation::ADDR, *this, nullptr) == nullptr);
|
||||
}
|
||||
|
||||
friend void swap(any &lhs, any &rhs) {
|
||||
any tmp{};
|
||||
lhs.vtable(operation::MOVE, lhs, &tmp);
|
||||
rhs.vtable(operation::MOVE, rhs, &lhs);
|
||||
lhs.vtable(operation::MOVE, tmp, &rhs);
|
||||
std::swap(lhs.vtable, rhs.vtable);
|
||||
}
|
||||
|
||||
private:
|
||||
vtable_type *vtable;
|
||||
union { void *instance; storage_type storage; };
|
||||
};
|
||||
|
||||
|
||||
template<typename Type>
|
||||
Type any_cast(const any &any) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(any.type() == type_id<Type>());
|
||||
return *static_cast<const Type *>(any.data());
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
Type any_cast(any &any) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(any.type() == type_id<Type>());
|
||||
return *static_cast<Type *>(any.data());
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
Type any_cast(any &&any) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(any.type() == type_id<Type>());
|
||||
return std::move(*static_cast<Type *>(any.data()));
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
const Type * any_cast(const any *any) ENTT_NOEXCEPT {
|
||||
return (any->type() == type_id<Type>() ? static_cast<const Type *>(any->data()) : nullptr);
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
Type * any_cast(any *any) ENTT_NOEXCEPT {
|
||||
return (any->type() == type_id<Type>() ? static_cast<Type *>(any->data()) : nullptr);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "config/version.h"
|
||||
#include "core/algorithm.hpp"
|
||||
#include "core/any.hpp"
|
||||
#include "core/attribute.h"
|
||||
#include "core/family.hpp"
|
||||
#include "core/hashed_string.hpp"
|
||||
|
||||
@@ -38,8 +38,7 @@ class meta_storage {
|
||||
using vtable_type = void *(const operation, const meta_storage &, meta_storage *);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr auto in_situ = sizeof(Type) <= sizeof(storage_type)
|
||||
&& std::is_nothrow_move_constructible_v<Type> && std::is_nothrow_copy_constructible_v<Type>;
|
||||
static constexpr auto in_situ = sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
|
||||
|
||||
template<typename Type>
|
||||
static void * basic_vtable(const operation op, const meta_storage &from, meta_storage *to) {
|
||||
@@ -103,7 +102,6 @@ class meta_storage {
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
meta_storage() ENTT_NOEXCEPT
|
||||
: vtable{&basic_vtable<void>},
|
||||
instance{}
|
||||
@@ -111,7 +109,8 @@ public:
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
explicit meta_storage(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
|
||||
: vtable{&basic_vtable<Type>}
|
||||
: vtable{&basic_vtable<Type>},
|
||||
instance{}
|
||||
{
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
if constexpr(in_situ<Type>) {
|
||||
|
||||
@@ -153,6 +153,7 @@ endif()
|
||||
# Test core
|
||||
|
||||
SETUP_BASIC_TEST(algorithm entt/core/algorithm.cpp)
|
||||
SETUP_BASIC_TEST(any entt/core/any.cpp)
|
||||
SETUP_BASIC_TEST(family entt/core/family.cpp)
|
||||
SETUP_BASIC_TEST(hashed_string entt/core/hashed_string.cpp)
|
||||
SETUP_BASIC_TEST(ident entt/core/ident.cpp)
|
||||
|
||||
545
test/entt/core/any.cpp
Normal file
545
test/entt/core/any.cpp
Normal file
@@ -0,0 +1,545 @@
|
||||
#include <algorithm>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/any.hpp>
|
||||
|
||||
struct fat {
|
||||
double value[4];
|
||||
inline static int counter = 0;
|
||||
|
||||
~fat() { ++counter; }
|
||||
|
||||
bool operator==(const fat &other) const {
|
||||
return std::equal(std::begin(value), std::end(value), std::begin(other.value), std::end(other.value));
|
||||
}
|
||||
};
|
||||
|
||||
struct empty {
|
||||
inline static int counter = 0;
|
||||
~empty() { ++counter; }
|
||||
};
|
||||
|
||||
TEST(Any, SBO) {
|
||||
entt::any any{'c'};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(any), 'c');
|
||||
}
|
||||
|
||||
TEST(Any, NoSBO) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{instance};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
}
|
||||
|
||||
TEST(Any, Empty) {
|
||||
entt::any any{};
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(any.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, SBOInPlaceTypeConstruction) {
|
||||
entt::any any{std::in_place_type<int>, 42};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
|
||||
auto other = any.ref();
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 42);
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
|
||||
TEST(Any, SBOAsRefConstruction) {
|
||||
int value = 42;
|
||||
entt::any any{std::ref(value)};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
ASSERT_EQ(any.data(), &value);
|
||||
|
||||
auto other = any.ref();
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 42);
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
|
||||
TEST(Any, SBOCopyConstruction) {
|
||||
entt::any any{42};
|
||||
entt::any other{any};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 42);
|
||||
}
|
||||
|
||||
TEST(Any, SBOCopyAssignment) {
|
||||
entt::any any{42};
|
||||
entt::any other{3};
|
||||
|
||||
other = any;
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 42);
|
||||
}
|
||||
|
||||
TEST(Any, SBOMoveConstruction) {
|
||||
entt::any any{42};
|
||||
entt::any other{std::move(any)};
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 42);
|
||||
}
|
||||
|
||||
TEST(Any, SBOMoveAssignment) {
|
||||
entt::any any{42};
|
||||
entt::any other{3};
|
||||
|
||||
other = std::move(any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 42);
|
||||
}
|
||||
|
||||
TEST(Any, SBODirectAssignment) {
|
||||
entt::any any{};
|
||||
any = 42;
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOInPlaceTypeConstruction) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{std::in_place_type<fat>, instance};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
|
||||
auto other = any.ref();
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), (fat{{.1, .2, .3, .4}}));
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOAsRefConstruction) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{std::ref(instance)};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
ASSERT_EQ(any.data(), &instance);
|
||||
|
||||
auto other = any.ref();
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), (fat{{.1, .2, .3, .4}}));
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOCopyConstruction) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{instance};
|
||||
entt::any other{any};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOCopyAssignment) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{instance};
|
||||
entt::any other{3};
|
||||
|
||||
other = any;
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOMoveConstruction) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{instance};
|
||||
entt::any other{std::move(any)};
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOMoveAssignment) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{instance};
|
||||
entt::any other{3};
|
||||
|
||||
other = std::move(any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBODirectAssignment) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{};
|
||||
any = instance;
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
}
|
||||
|
||||
TEST(Any, VoidInPlaceTypeConstruction) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, VoidCopyConstruction) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
entt::any other{any};
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_FALSE(other.type());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, VoidCopyAssignment) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
entt::any other{std::in_place_type<void>};
|
||||
|
||||
other = any;
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_FALSE(other.type());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, VoidMoveConstruction) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
entt::any other{std::move(any)};
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_FALSE(other.type());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, VoidMoveAssignment) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
entt::any other{std::in_place_type<void>};
|
||||
|
||||
other = std::move(any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_FALSE(other.type());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, SBOMoveInvalidate) {
|
||||
entt::any any{42};
|
||||
entt::any other{std::move(any)};
|
||||
entt::any valid = std::move(other);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_TRUE(valid);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOMoveInvalidate) {
|
||||
fat instance{{.1, .2, .3, .4}};
|
||||
entt::any any{instance};
|
||||
entt::any other{std::move(any)};
|
||||
entt::any valid = std::move(other);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_TRUE(valid);
|
||||
}
|
||||
|
||||
TEST(Any, VoidMoveInvalidate) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
entt::any other{std::move(any)};
|
||||
entt::any valid = std::move(other);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(valid);
|
||||
}
|
||||
|
||||
TEST(Any, SBODestruction) {
|
||||
{
|
||||
entt::any any{empty{}};
|
||||
empty::counter = 0;
|
||||
}
|
||||
|
||||
ASSERT_EQ(empty::counter, 1);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBODestruction) {
|
||||
{
|
||||
entt::any any{fat{}};
|
||||
fat::counter = 0;
|
||||
}
|
||||
|
||||
ASSERT_EQ(fat::counter, 1);
|
||||
}
|
||||
|
||||
TEST(Any, VoidDestruction) {
|
||||
// just let asan tell us if everything is ok here
|
||||
[[maybe_unused]] entt::any any{std::in_place_type<void>};
|
||||
}
|
||||
|
||||
TEST(Any, Emplace) {
|
||||
entt::any any{};
|
||||
any.emplace<int>(42);
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 42);
|
||||
}
|
||||
|
||||
TEST(Any, EmplaceVoid) {
|
||||
entt::any any{};
|
||||
any.emplace<void>();
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.type());
|
||||
}
|
||||
|
||||
TEST(Any, SBOSwap) {
|
||||
entt::any lhs{'c'};
|
||||
entt::any rhs{42};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<char>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(lhs), 42);
|
||||
ASSERT_EQ(entt::any_cast<char>(rhs), 'c');
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOSwap) {
|
||||
entt::any lhs{fat{{.1, .2, .3, .4}}};
|
||||
entt::any rhs{fat{{.4, .3, .2, .1}}};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{{.4, .3, .2, .1}}));
|
||||
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{{.1, .2, .3, .4}}));
|
||||
}
|
||||
|
||||
TEST(Any, VoidSwap) {
|
||||
entt::any lhs{std::in_place_type<void>};
|
||||
entt::any rhs{std::in_place_type<void>};
|
||||
const auto *pre = lhs.data();
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(pre, lhs.data());
|
||||
}
|
||||
|
||||
TEST(Any, SBOWithNoSBOSwap) {
|
||||
entt::any lhs{fat{{.1, .2, .3, .4}}};
|
||||
entt::any rhs{'c'};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{{.1, .2, .3, .4}}));
|
||||
}
|
||||
|
||||
TEST(Any, SBOWithRefSwap) {
|
||||
int value = 3;
|
||||
entt::any lhs{std::ref(value)};
|
||||
entt::any rhs{'c'};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
ASSERT_EQ(entt::any_cast<int>(rhs), 3);
|
||||
ASSERT_EQ(rhs.data(), &value);
|
||||
}
|
||||
|
||||
TEST(Any, SBOWithEmptySwap) {
|
||||
entt::any lhs{'c'};
|
||||
entt::any rhs{};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<char>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(rhs), 'c');
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
}
|
||||
|
||||
TEST(Any, SBOWithVoidSwap) {
|
||||
entt::any lhs{'c'};
|
||||
entt::any rhs{std::in_place_type<void>};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<char>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(rhs), 'c');
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOWithRefSwap) {
|
||||
int value = 3;
|
||||
entt::any lhs{std::ref(value)};
|
||||
entt::any rhs{fat{{.1, .2, .3, .4}}};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{{.1, .2, .3, .4}}));
|
||||
ASSERT_EQ(entt::any_cast<int>(rhs), 3);
|
||||
ASSERT_EQ(rhs.data(), &value);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOWithEmptySwap) {
|
||||
entt::any lhs{fat{{.1, .2, .3, .4}}};
|
||||
entt::any rhs{};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{{.1, .2, .3, .4}}));
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{{.1, .2, .3, .4}}));
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOWithVoidSwap) {
|
||||
entt::any lhs{fat{{.1, .2, .3, .4}}};
|
||||
entt::any rhs{std::in_place_type<void>};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{{.1, .2, .3, .4}}));
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{{.1, .2, .3, .4}}));
|
||||
}
|
||||
|
||||
TEST(Any, AnyCastTemporary) {
|
||||
ASSERT_EQ(entt::any_cast<int>(42), 42);
|
||||
}
|
||||
@@ -35,6 +35,7 @@ struct fat_t: empty_t {
|
||||
|
||||
int *foo{nullptr};
|
||||
int *bar{nullptr};
|
||||
double gnam[4];
|
||||
};
|
||||
|
||||
struct not_comparable_t {
|
||||
|
||||
Reference in New Issue
Block a user