meta: split meta_storage from meta_any

This commit is contained in:
Michele Caini
2020-06-18 14:31:06 +02:00
parent da212dc2a4
commit e113fa53a4
4 changed files with 202 additions and 129 deletions

1
TODO
View File

@@ -31,3 +31,4 @@ Next:
- add meta_handle::operator-> that returns a meta_any * (easier to use directly)
- use a dedicate class template to specialize meta views for a better support to customizations
- remove operator* from meta_any, meta_handle
- add const meta container support to meta any

View File

@@ -374,8 +374,8 @@ public:
type,
nullptr,
&internal::meta_info<Base>::resolve,
[](void *instance) ENTT_NOEXCEPT -> void * {
return static_cast<Base *>(static_cast<Type *>(instance));
[](const void *instance) ENTT_NOEXCEPT -> const void * {
return static_cast<const Base *>(static_cast<const Type *>(instance));
}
};

View File

@@ -3,6 +3,7 @@
#include <cstddef>
#include <functional>
#include <iterator>
#include <type_traits>
#include <utility>
@@ -29,6 +30,172 @@ struct meta_handle;
namespace internal {
class meta_storage {
using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
using copy_fn_type = void(meta_storage &, const meta_storage &);
using steal_fn_type = void(meta_storage &, meta_storage &);
using destroy_fn_type = void(meta_storage &);
template<typename Type, typename = std::void_t<>>
struct type_traits {
template<typename... Args>
static void instance(meta_storage &buffer, Args &&... args) {
buffer.instance = new Type{std::forward<Args>(args)...};
new (&buffer.storage) Type *{static_cast<Type *>(buffer.instance)};
}
static void destroy(meta_storage &buffer) {
delete static_cast<Type *>(buffer.instance);
}
static void copy(meta_storage &to, const meta_storage &from) {
to.instance = new Type{*static_cast<const Type *>(from.instance)};
new (&to.storage) Type *{static_cast<Type *>(to.instance)};
}
static void steal(meta_storage &to, meta_storage &from) {
new (&to.storage) Type *{static_cast<Type *>(from.instance)};
to.instance = from.instance;
}
};
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(meta_storage &buffer, Args &&... args) {
buffer.instance = new (&buffer.storage) Type{std::forward<Args>(args)...};
}
static void destroy(meta_storage &buffer) {
static_cast<Type *>(buffer.instance)->~Type();
}
static void copy(meta_storage &to, const meta_storage &from) {
to.instance = new (&to.storage) Type{*static_cast<const Type *>(from.instance)};
}
static void steal(meta_storage &to, meta_storage &from) {
to.instance = new (&to.storage) Type{std::move(*static_cast<Type *>(from.instance))};
destroy(from);
}
};
public:
/*! @brief Default constructor. */
meta_storage() ENTT_NOEXCEPT
: storage{},
instance{},
destroy_fn{},
copy_fn{},
steal_fn{}
{}
template<typename Type, typename... Args>
explicit meta_storage(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
: meta_storage{}
{
if constexpr(!std::is_void_v<Type>) {
type_traits<Type>::instance(*this, std::forward<Args>(args)...);
destroy_fn = &type_traits<Type>::destroy;
copy_fn = &type_traits<Type>::copy;
steal_fn = &type_traits<Type>::steal;
}
}
template<typename Type>
meta_storage(std::reference_wrapper<Type> value)
: meta_storage{}
{
instance = &value.get();
}
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_storage>>>
meta_storage(Type &&value)
: meta_storage{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
{}
meta_storage(const meta_storage &other)
: meta_storage{}
{
(other.copy_fn ? other.copy_fn : [](auto &to, const auto &from) { to.instance = from.instance; })(*this, other);
destroy_fn = other.destroy_fn;
copy_fn = other.copy_fn;
steal_fn = other.steal_fn;
}
meta_storage(meta_storage &&other)
: meta_storage{}
{
swap(*this, other);
}
~meta_storage() {
if(destroy_fn) {
destroy_fn(*this);
}
}
template<typename Type>
meta_storage & operator=(Type &&value) {
return (*this = meta_storage{std::forward<Type>(value)});
}
meta_storage & operator=(meta_storage other) {
swap(other, *this);
return *this;
}
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
return instance;
}
[[nodiscard]] void * data() ENTT_NOEXCEPT {
return const_cast<void *>(std::as_const(*this).data());
}
template<typename Type, typename... Args>
void emplace(Args &&... args) {
*this = meta_storage{std::in_place_type<Type>, std::forward<Args>(args)...};
}
[[nodiscard]] meta_storage ref() const ENTT_NOEXCEPT {
meta_storage other{};
other.instance = instance;
return other;
}
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
return !(instance == nullptr);
}
friend void swap(meta_storage &lhs, meta_storage &rhs) {
if(lhs.steal_fn && rhs.steal_fn) {
meta_storage buffer{};
lhs.steal_fn(buffer, lhs);
rhs.steal_fn(lhs, rhs);
lhs.steal_fn(rhs, buffer);
} else if(lhs.steal_fn) {
lhs.steal_fn(rhs, lhs);
} else if(rhs.steal_fn) {
rhs.steal_fn(lhs, rhs);
} else {
std::swap(lhs.instance, rhs.instance);
}
std::swap(lhs.destroy_fn, rhs.destroy_fn);
std::swap(lhs.copy_fn, rhs.copy_fn);
std::swap(lhs.steal_fn, rhs.steal_fn);
}
private:
storage_type storage;
void *instance;
destroy_fn_type *destroy_fn;
copy_fn_type *copy_fn;
steal_fn_type *steal_fn;
};
struct meta_type_node;
@@ -43,7 +210,7 @@ struct meta_base_node {
meta_type_node * const parent;
meta_base_node * next;
meta_type_node *(* const type)() ENTT_NOEXCEPT;
void *(* const cast)(void *) ENTT_NOEXCEPT;
const void *(* const cast)(const void *) ENTT_NOEXCEPT;
};

View File

@@ -79,63 +79,6 @@ private:
* of memory allocations if possible. This should improve overall performance.
*/
class meta_any {
using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
using copy_fn_type = void(meta_any &, const meta_any &);
using steal_fn_type = void(meta_any &, meta_any &);
using destroy_fn_type = void(meta_any &);
template<typename Type, typename = std::void_t<>>
struct type_traits {
template<typename... Args>
static void instance(meta_any &any, Args &&... args) {
any.instance = new Type{std::forward<Args>(args)...};
new (&any.storage) Type *{static_cast<Type *>(any.instance)};
}
static void destroy(meta_any &any) {
if(const auto * const node = internal::meta_info<Type>::resolve(); node->dtor) {
node->dtor->invoke(any.instance);
}
delete static_cast<Type *>(any.instance);
}
static void copy(meta_any &to, const meta_any &from) {
to.instance = new Type{*static_cast<const Type *>(from.instance)};
new (&to.storage) Type *{static_cast<Type *>(to.instance)};
}
static void steal(meta_any &to, meta_any &from) {
new (&to.storage) Type *{static_cast<Type *>(from.instance)};
to.instance = from.instance;
}
};
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(meta_any &any, Args &&... args) {
any.instance = new (&any.storage) Type{std::forward<Args>(args)...};
}
static void destroy(meta_any &any) {
if(const auto * const node = internal::meta_info<Type>::resolve(); node->dtor) {
node->dtor->invoke(any.instance);
}
static_cast<Type *>(any.instance)->~Type();
}
static void copy(meta_any &to, const meta_any &from) {
to.instance = new (&to.storage) Type{*static_cast<const Type *>(from.instance)};
}
static void steal(meta_any &to, meta_any &from) {
to.instance = new (&to.storage) Type{std::move(*static_cast<Type *>(from.instance))};
destroy(from);
}
};
template<typename, typename = void>
struct container_view {
[[nodiscard]] static meta_container::meta_view * instance() {
@@ -293,11 +236,7 @@ public:
/*! @brief Default constructor. */
meta_any() ENTT_NOEXCEPT
: storage{},
instance{},
node{},
destroy_fn{},
copy_fn{},
steal_fn{},
cview{}
{}
@@ -309,18 +248,10 @@ public:
*/
template<typename Type, typename... Args>
explicit meta_any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
: meta_any{}
{
node = internal::meta_info<Type>::resolve();
if constexpr(!std::is_void_v<Type>) {
type_traits<Type>::instance(*this, std::forward<Args>(args)...);
destroy_fn = &type_traits<Type>::destroy;
copy_fn = &type_traits<Type>::copy;
steal_fn = &type_traits<Type>::steal;
cview = container_view<Type>::instance();
}
}
: storage(std::in_place_type<Type>, std::forward<Args>(args)...),
node{internal::meta_info<Type>::resolve()},
cview{container_view<Type>::instance()}
{}
/**
* @brief Constructs a meta any that holds an unmanaged object.
@@ -329,12 +260,10 @@ public:
*/
template<typename Type>
meta_any(std::reference_wrapper<Type> value)
: meta_any{}
{
node = internal::meta_info<Type>::resolve();
instance = &value.get();
cview = container_view<Type>::instance();
}
: storage{value},
node{internal::meta_info<Type>::resolve()},
cview{container_view<Type>::instance()}
{}
/**
* @brief Constructs a meta any from a given value.
@@ -351,15 +280,10 @@ public:
* @param other The instance to copy from.
*/
meta_any(const meta_any &other)
: meta_any{}
{
node = other.node;
(other.copy_fn ? other.copy_fn : [](meta_any &to, const meta_any &from) { to.instance = from.instance; })(*this, other);
destroy_fn = other.destroy_fn;
copy_fn = other.copy_fn;
steal_fn = other.steal_fn;
cview = other.cview;
}
: storage{other.storage},
node{other.node},
cview{other.cview}
{}
/**
* @brief Move constructor.
@@ -373,8 +297,8 @@ public:
/*! @brief Frees the internal storage, whatever it means. */
~meta_any() {
if(destroy_fn) {
destroy_fn(*this);
if(node && node->dtor) {
node->dtor->invoke(storage.data());
}
}
@@ -410,12 +334,12 @@ public:
* @return An opaque pointer the contained instance, if any.
*/
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
return instance;
return storage.data();
}
/*! @copydoc data */
[[nodiscard]] void * data() ENTT_NOEXCEPT {
return const_cast<void *>(std::as_const(*this).data());
return storage.data();
}
/**
@@ -425,13 +349,13 @@ public:
*/
template<typename Type>
[[nodiscard]] const Type * try_cast() const {
void *ret = nullptr;
const void *ret = nullptr;
if(node) {
if(const auto type_id = internal::meta_info<Type>::resolve()->type_id; node->type_id == type_id) {
ret = instance;
ret = storage.data();
} else if(const auto *base = internal::find_if<&internal::meta_type_node::base>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); base) {
ret = base->cast(instance);
ret = base->cast(storage.data());
}
}
@@ -485,7 +409,7 @@ public:
if(const auto type_id = internal::meta_info<Type>::resolve()->type_id; node->type_id == type_id) {
any = *this;
} else if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); conv) {
any = conv->conv(instance);
any = conv->conv(storage.data());
}
}
@@ -527,19 +451,19 @@ public:
* @return A meta any that shares a reference to an unmanaged object.
*/
[[nodiscard]] meta_any ref() const ENTT_NOEXCEPT {
meta_any alias{};
alias.node = node;
alias.instance = instance;
alias.cview = cview;
return alias;
meta_any other{};
other.node = node;
other.storage = storage.ref();
other.cview = cview;
return other;
}
/**
* @brief Returns a container view.
* @return A container view for the underlying object.
*/
[[nodiscard]] meta_container view() const ENTT_NOEXCEPT {
return { cview, instance };
[[nodiscard]] meta_container view() ENTT_NOEXCEPT {
return { cview, storage.data() };
}
/**
@@ -564,7 +488,7 @@ public:
* @return False if the two objects differ in their content, true otherwise.
*/
[[nodiscard]] bool operator==(const meta_any &other) const {
return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id && node->compare(instance, other.instance));
return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id && node->compare(storage.data(), other.storage.data()));
}
/**
@@ -573,33 +497,14 @@ public:
* @param rhs A valid meta any object.
*/
friend void swap(meta_any &lhs, meta_any &rhs) {
if(lhs.steal_fn && rhs.steal_fn) {
meta_any buffer{};
lhs.steal_fn(buffer, lhs);
rhs.steal_fn(lhs, rhs);
lhs.steal_fn(rhs, buffer);
} else if(lhs.steal_fn) {
lhs.steal_fn(rhs, lhs);
} else if(rhs.steal_fn) {
rhs.steal_fn(lhs, rhs);
} else {
std::swap(lhs.instance, rhs.instance);
}
std::swap(lhs.node, rhs.node);
std::swap(lhs.destroy_fn, rhs.destroy_fn);
std::swap(lhs.copy_fn, rhs.copy_fn);
std::swap(lhs.steal_fn, rhs.steal_fn);
std::swap(lhs.storage, rhs.storage);
std::swap(lhs.cview, rhs.cview);
}
private:
storage_type storage;
void *instance;
internal::meta_storage storage;
const internal::meta_type_node *node;
destroy_fn_type *destroy_fn;
copy_fn_type *copy_fn;
steal_fn_type *steal_fn;
meta_container::meta_view *cview;
};
@@ -896,7 +801,7 @@ struct meta_base {
* @param instance The instance to cast.
* @return An opaque pointer to the base type.
*/
[[nodiscard]] void * cast(void *instance) const ENTT_NOEXCEPT {
[[nodiscard]] const void * cast(const void *instance) const ENTT_NOEXCEPT {
return node->cast(instance);
}