meta_any meets void
This commit is contained in:
@@ -303,75 +303,111 @@ class meta_any {
|
||||
friend class meta_handle;
|
||||
|
||||
using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
|
||||
using compare_fn_type = bool(const void *, const void *) ENTT_NOEXCEPT;
|
||||
using compare_fn_type = bool(const void *, const void *);
|
||||
using copy_fn_type = void *(storage_type &, const void *);
|
||||
using destroy_fn_type = void(storage_type &);
|
||||
using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT;
|
||||
using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename Type, typename = std::void_t<>>
|
||||
struct type_traits {
|
||||
using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
|
||||
|
||||
template<typename... Args>
|
||||
static void * instance(storage_type &storage, Args &&... args) {
|
||||
auto chunk = std::make_unique<chunk_type>();
|
||||
auto *instance = new (chunk.get()) Type{std::forward<Args>(args)...};
|
||||
new (&storage) chunk_type *{chunk.get()};
|
||||
chunk.release();
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void destroy(storage_type &storage) {
|
||||
auto *node = internal::meta_info<Type>::resolve();
|
||||
auto *chunk = *reinterpret_cast<chunk_type **>(&storage);
|
||||
auto *instance = reinterpret_cast<Type *>(chunk);
|
||||
node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
|
||||
delete chunk;
|
||||
}
|
||||
|
||||
static void * copy(storage_type &storage, const void *instance) {
|
||||
auto chunk = std::make_unique<chunk_type>();
|
||||
new (&storage) chunk_type *{chunk.get()};
|
||||
auto *other = new (chunk.get()) Type{*static_cast<const Type *>(instance)};
|
||||
chunk.release();
|
||||
return other;
|
||||
}
|
||||
|
||||
static void * steal(storage_type &to, storage_type &from, destroy_fn_type *, void *instance) ENTT_NOEXCEPT {
|
||||
auto *chunk = *reinterpret_cast<chunk_type **>(&from);
|
||||
new (&to) chunk_type *{chunk};
|
||||
chunk->~chunk_type();
|
||||
return instance;
|
||||
}
|
||||
|
||||
static bool compare(const void *lhs, const void *rhs) {
|
||||
return meta_any::compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
static auto compare(int, const Type &lhs, const Type &rhs) ENTT_NOEXCEPT
|
||||
-> decltype(lhs == rhs, bool{}) {
|
||||
struct type_traits<Type, std::enable_if_t<std::is_void_v<Type>>> {
|
||||
static void * instance(storage_type &) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void destroy(storage_type &) {}
|
||||
|
||||
static void * copy(storage_type &, const void *) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void * steal(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool compare(const void *, const void *) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct type_traits<Type, std::enable_if_t<sizeof(Type) <= sizeof(void *) && std::is_nothrow_move_constructible_v<Type>>> {
|
||||
template<typename... Args>
|
||||
static void * instance(storage_type &storage, Args &&... args) {
|
||||
return new (&storage) Type{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
static void destroy(storage_type &storage) {
|
||||
auto *node = internal::meta_info<Type>::resolve();
|
||||
auto *instance = reinterpret_cast<Type *>(&storage);
|
||||
node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
|
||||
}
|
||||
|
||||
static void * copy(storage_type &storage, const void *instance) {
|
||||
return new (&storage) Type{*static_cast<const Type *>(instance)};
|
||||
}
|
||||
|
||||
static void * steal(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn, void *) ENTT_NOEXCEPT {
|
||||
void *instance = new (&to) Type{std::move(*reinterpret_cast<Type *>(&from))};
|
||||
destroy_fn(from);
|
||||
return instance;
|
||||
}
|
||||
|
||||
static bool compare(const void *lhs, const void *rhs) {
|
||||
return meta_any::compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
static auto compare(int, const Type &lhs, const Type &rhs) -> decltype(lhs == rhs, bool{}) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static bool compare(char, const Type &lhs, const Type &rhs) ENTT_NOEXCEPT {
|
||||
static bool compare(char, const Type &lhs, const Type &rhs) {
|
||||
return &lhs == &rhs;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static bool compare(const void *lhs, const void *rhs) ENTT_NOEXCEPT {
|
||||
return compare(0, *static_cast<const Type *>(lhs), *static_cast<const Type *>(rhs));
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void * copy_storage(storage_type &storage, const void *instance) {
|
||||
return new (&storage) Type{*static_cast<const Type *>(instance)};
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void * copy_object(storage_type &storage, const void *instance) {
|
||||
using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
|
||||
auto chunk = std::make_unique<chunk_type>();
|
||||
new (&storage) chunk_type *{chunk.get()};
|
||||
auto *other = new (chunk.get()) Type{*static_cast<const Type *>(instance)};
|
||||
chunk.release();
|
||||
return other;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void * steal_storage(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn, void *) noexcept {
|
||||
void *instance = new (&to) Type{std::move(*reinterpret_cast<Type *>(&from))};
|
||||
destroy_fn(from);
|
||||
return instance;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void * steal_object(storage_type &to, storage_type &from, destroy_fn_type *, void *instance) noexcept {
|
||||
using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
|
||||
auto *chunk = *reinterpret_cast<chunk_type **>(&from);
|
||||
new (&to) chunk_type *{chunk};
|
||||
chunk->~chunk_type();
|
||||
return instance;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void destroy_storage(storage_type &storage) {
|
||||
auto *node = internal::meta_info<Type>::resolve();
|
||||
auto *instance = reinterpret_cast<Type *>(&storage);
|
||||
node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void destroy_object(storage_type &storage) {
|
||||
using chunk_type = std::aligned_storage_t<sizeof(Type), alignof(Type)>;
|
||||
auto *node = internal::meta_info<Type>::resolve();
|
||||
auto *chunk = *reinterpret_cast<chunk_type **>(&storage);
|
||||
auto *instance = reinterpret_cast<Type *>(chunk);
|
||||
node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance);
|
||||
delete chunk;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
meta_any() ENTT_NOEXCEPT
|
||||
@@ -401,30 +437,14 @@ public:
|
||||
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>>;
|
||||
using traits_type = type_traits<actual_type>;
|
||||
|
||||
node = internal::meta_info<Type>::resolve();
|
||||
|
||||
compare_fn = &compare<actual_type>;
|
||||
|
||||
constexpr auto sbo_allowed = sizeof(actual_type) <= sizeof(void *)
|
||||
&& std::is_nothrow_move_constructible_v<actual_type>;
|
||||
|
||||
if constexpr(sbo_allowed) {
|
||||
instance = new (&storage) actual_type{std::forward<Args>(args)...};
|
||||
destroy_fn = &destroy_storage<actual_type>;
|
||||
copy_fn = ©_storage<actual_type>;
|
||||
steal_fn = &steal_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 = ©_object<actual_type>;
|
||||
steal_fn = &steal_object<actual_type>;
|
||||
}
|
||||
instance = traits_type::instance(storage, std::forward<Args>(args)...);
|
||||
destroy_fn = &traits_type::destroy;
|
||||
copy_fn = &traits_type::copy;
|
||||
steal_fn = &traits_type::steal;
|
||||
compare_fn = &traits_type::compare;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -622,7 +642,7 @@ public:
|
||||
* @return False if the container is empty, true otherwise.
|
||||
*/
|
||||
explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return instance;
|
||||
return destroy_fn;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -632,7 +652,7 @@ public:
|
||||
* otherwise.
|
||||
*/
|
||||
bool operator==(const meta_any &other) const ENTT_NOEXCEPT {
|
||||
return (!instance && !other.instance) || (instance && other.instance && node == other.node && compare_fn(instance, other.instance));
|
||||
return (!compare_fn && !other.compare_fn) || (compare_fn && other.compare_fn && node == other.node && compare_fn(instance, other.instance));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -425,6 +425,61 @@ TEST_F(Meta, MetaAnyNoSBOMoveAssignment) {
|
||||
ASSERT_NE(other, fat_type{});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidInPlaceConstruction) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.can_cast<void>());
|
||||
ASSERT_FALSE(any.can_cast<char>());
|
||||
ASSERT_EQ(any.data(), nullptr);
|
||||
ASSERT_EQ(std::as_const(any).data(), nullptr);
|
||||
ASSERT_EQ(any, entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidCopyConstruction) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
entt::meta_any other{any};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.can_cast<void>());
|
||||
ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidCopyAssignment) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
entt::meta_any other{std::in_place_type<void>};
|
||||
|
||||
other = any;
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.can_cast<void>());
|
||||
ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidMoveConstruction) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
entt::meta_any other{std::move(any)};
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.can_cast<void>());
|
||||
ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidMoveAssignment) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
entt::meta_any other{std::in_place_type<void>};
|
||||
|
||||
other = std::move(any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.can_cast<void>());
|
||||
ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnySBOMoveInvalidate) {
|
||||
entt::meta_any any{42};
|
||||
entt::meta_any other{std::move(any)};
|
||||
@@ -447,6 +502,16 @@ TEST_F(Meta, MetaAnyNoSBOMoveInvalidate) {
|
||||
ASSERT_TRUE(valid);
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidMoveInvalidate) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
entt::meta_any other{std::move(any)};
|
||||
entt::meta_any valid = std::move(other);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_TRUE(valid);
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnySBODestruction) {
|
||||
ASSERT_EQ(empty_type::counter, 0);
|
||||
{ entt::meta_any any{empty_type{}}; }
|
||||
@@ -459,6 +524,11 @@ TEST_F(Meta, MetaAnyNoSBODestruction) {
|
||||
ASSERT_EQ(fat_type::counter, 1);
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidDestruction) {
|
||||
// just let asan tell us if everything is ok here
|
||||
[[maybe_unused]] entt::meta_any any{std::in_place_type<void>};
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyEmplace) {
|
||||
entt::meta_any any{};
|
||||
any.emplace<int>(42);
|
||||
@@ -475,6 +545,17 @@ TEST_F(Meta, MetaAnyEmplace) {
|
||||
ASSERT_NE(any, entt::meta_any{3});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyEmplaceVoid) {
|
||||
entt::meta_any any{};
|
||||
any.emplace<void>();
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.can_cast<void>());
|
||||
ASSERT_EQ(any.data(), nullptr);
|
||||
ASSERT_EQ(std::as_const(any).data(), nullptr);
|
||||
ASSERT_EQ(any, (entt::meta_any{std::in_place_type<void>}));
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnySBOSwap) {
|
||||
entt::meta_any lhs{'c'};
|
||||
entt::meta_any rhs{42};
|
||||
@@ -498,6 +579,16 @@ TEST_F(Meta, MetaAnyNoSBOSwap) {
|
||||
ASSERT_EQ(rhs.cast<fat_type>().bar, &i);
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyVoidSwap) {
|
||||
entt::meta_any lhs{std::in_place_type<void>};
|
||||
entt::meta_any rhs{std::in_place_type<void>};
|
||||
const auto *pre = lhs.data();
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(pre, lhs.data());
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnySBOWithNoSBOSwap) {
|
||||
int value = 42;
|
||||
entt::meta_any lhs{fat_type{&value}};
|
||||
@@ -529,6 +620,17 @@ TEST_F(Meta, MetaAnySBOWithEmptySwap) {
|
||||
ASSERT_EQ(lhs.cast<char>(), 'c');
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnySBOWithVoidSwap) {
|
||||
entt::meta_any lhs{'c'};
|
||||
entt::meta_any rhs{std::in_place_type<void>};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_TRUE(lhs.can_cast<void>());
|
||||
ASSERT_TRUE(rhs.can_cast<char>());
|
||||
ASSERT_EQ(rhs.cast<char>(), 'c');
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyNoSBOWithEmptySwap) {
|
||||
int i;
|
||||
entt::meta_any lhs{fat_type{&i}};
|
||||
@@ -543,6 +645,20 @@ TEST_F(Meta, MetaAnyNoSBOWithEmptySwap) {
|
||||
ASSERT_EQ(lhs.cast<fat_type>().bar, &i);
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyNoSBOWithVoidSwap) {
|
||||
int i;
|
||||
entt::meta_any lhs{fat_type{&i}};
|
||||
entt::meta_any rhs{std::in_place_type<void>};
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(rhs.cast<fat_type>().bar, &i);
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
ASSERT_EQ(lhs.cast<fat_type>().bar, &i);
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyComparable) {
|
||||
entt::meta_any any{'c'};
|
||||
|
||||
@@ -570,6 +686,21 @@ TEST_F(Meta, MetaAnyNotComparable) {
|
||||
ASSERT_TRUE(any != entt::meta_any{});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyCompareVoid) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
|
||||
ASSERT_EQ(any, any);
|
||||
ASSERT_EQ(any, entt::meta_any{std::in_place_type<void>});
|
||||
ASSERT_NE(any, entt::meta_any{'a'});
|
||||
ASSERT_NE(any, entt::meta_any{});
|
||||
|
||||
ASSERT_TRUE(any == any);
|
||||
ASSERT_TRUE(any == entt::meta_any{std::in_place_type<void>});
|
||||
ASSERT_FALSE(any == entt::meta_any{'a'});
|
||||
ASSERT_TRUE(any != entt::meta_any{'a'});
|
||||
ASSERT_TRUE(any != entt::meta_any{});
|
||||
}
|
||||
|
||||
TEST_F(Meta, MetaAnyCast) {
|
||||
entt::meta_any any{derived_type{}};
|
||||
entt::meta_handle handle{any};
|
||||
|
||||
Reference in New Issue
Block a user