meta_any meets void

This commit is contained in:
Michele Caini
2019-07-05 23:03:01 +02:00
parent 530407f9b2
commit 260b9a8d0d
2 changed files with 234 additions and 83 deletions

View File

@@ -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 = &copy_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 = &copy_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));
}
/**

View File

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