any: drastically reduced the number of instantiations/size of vtable

This commit is contained in:
Michele Caini
2021-05-02 23:34:28 +02:00
parent b9bf1a234e
commit e6ddd5d9c2

View File

@@ -5,7 +5,6 @@
#include <cstddef>
#include <functional>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
#include "../config/config.h"
@@ -25,7 +24,8 @@ namespace entt {
*/
template<std::size_t Len, std::size_t Align>
class basic_any {
enum class operation: std::uint8_t { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE };
enum class operation: std::uint8_t { COPY, MOVE, DTOR, COMP, ADDR, CADDR, TYPE };
enum class policy: std::uint8_t { OWNED, REF, CREF };
using storage_type = std::aligned_storage_t<Len + !Len, Align>;
using vtable_type = const void *(const operation, const basic_any &, void *);
@@ -33,6 +33,19 @@ class basic_any {
template<typename Type>
static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
template<typename Type>
[[nodiscard]] static constexpr policy type_to_policy() {
if constexpr(std::is_lvalue_reference_v<Type>) {
if constexpr(std::is_const_v<std::remove_reference_t<Type>>) {
return policy::CREF;
} else {
return policy::REF;
}
} else {
return policy::OWNED;
}
}
template<typename Type>
[[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
@@ -44,76 +57,50 @@ class basic_any {
template<typename Type>
static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] void *to) {
static_assert(std::is_same_v<std::remove_reference_t<std::remove_const_t<Type>>, Type>, "Invalid type");
if constexpr(!std::is_void_v<Type>) {
if constexpr(!std::is_lvalue_reference_v<Type> && in_situ<Type>) {
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
const auto *instance = std::launder(reinterpret_cast<const Type *>(&from.storage));
#else
const auto *instance = reinterpret_cast<const Type *>(&from.storage);
#endif
const Type *instance = (in_situ<Type> && from.mode == policy::OWNED)
? ENTT_LAUNDER(reinterpret_cast<const Type *>(&from.storage))
: static_cast<const Type *>(from.instance);
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(to)->emplace<Type>(*instance);
}
break;
case operation::MOVE:
new (&static_cast<basic_any *>(to)->storage) Type{std::move(*const_cast<Type *>(instance))};
break;
case operation::DTOR:
const_cast<Type *>(instance)->~Type();
break;
case operation::COMP:
return compare<Type>(instance, (*static_cast<const basic_any **>(to))->data()) ? to : nullptr;
case operation::ADDR:
case operation::CADDR:
return instance;
case operation::REF:
static_cast<basic_any *>(to)->vtable = basic_vtable<Type &>;
break;
case operation::CREF:
static_cast<basic_any *>(to)->vtable = basic_vtable<const Type &>;
break;
case operation::TYPE:
*static_cast<type_info *>(to) = type_id<Type>();
break;
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(to)->emplace<Type>(*instance);
}
} else { /* either a reference or a dynamically allocated object */
const auto *instance = static_cast<const std::decay_t<Type> *>(from.instance);
switch(op) {
case operation::COPY:
if constexpr(std::is_copy_constructible_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
static_cast<basic_any *>(to)->emplace<std::decay_t<Type>>(*instance);
break;
case operation::MOVE:
if constexpr(in_situ<Type>) {
if(from.mode == policy::OWNED) {
return new (&static_cast<basic_any *>(to)->storage) Type{std::move(*const_cast<Type *>(instance))};
}
break;
case operation::MOVE:
static_cast<basic_any *>(to)->instance = std::exchange(const_cast<basic_any &>(from).instance, nullptr);
break;
case operation::DTOR:
if constexpr(std::is_array_v<Type>) {
}
return (static_cast<basic_any *>(to)->instance = std::exchange(const_cast<basic_any &>(from).instance, nullptr));
case operation::DTOR:
if(from.mode == policy::OWNED) {
if constexpr(in_situ<Type>) {
instance->~Type();
} else if constexpr(std::is_array_v<Type>) {
delete[] instance;
} else if constexpr(!std::is_lvalue_reference_v<Type>) {
} else {
delete instance;
}
break;
case operation::COMP:
return compare<std::remove_const_t<std::remove_reference_t<Type>>>(instance, (*static_cast<const basic_any **>(to))->data()) ? to : nullptr;
case operation::ADDR:
return std::is_const_v<std::remove_reference_t<Type>> ? nullptr : instance;
case operation::CADDR:
return instance;
case operation::REF:
static_cast<basic_any *>(to)->vtable = basic_vtable<Type &>;
break;
case operation::CREF:
static_cast<basic_any *>(to)->vtable = basic_vtable<const std::remove_reference_t<Type> &>;
break;
case operation::TYPE:
*static_cast<type_info *>(to) = type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
break;
}
break;
case operation::COMP:
return compare<Type>(instance, (*static_cast<const basic_any **>(to))->data()) ? to : nullptr;
case operation::ADDR:
if(from.mode == policy::CREF) {
return nullptr;
}
[[fallthrough]];
case operation::CADDR:
return instance;
case operation::TYPE:
*static_cast<type_info *>(to) = type_id<Type>();
break;
}
}
@@ -142,6 +129,12 @@ class basic_any {
}
}
basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
: instance{other.data()},
vtable{other.vtable},
mode{pol}
{}
public:
/*! @brief Size of the internal storage. */
static constexpr auto length = Len;
@@ -151,7 +144,8 @@ public:
/*! @brief Default constructor. */
basic_any() ENTT_NOEXCEPT
: instance{},
vtable{&basic_vtable<void>}
vtable{&basic_vtable<void>},
mode{policy::OWNED}
{}
/**
@@ -163,7 +157,8 @@ public:
template<typename Type, typename... Args>
explicit basic_any(std::in_place_type_t<Type>, Args &&... args)
: instance{},
vtable{&basic_vtable<Type>}
vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>},
mode{type_to_policy<Type>()}
{
initialize<Type>(std::forward<Args>(args)...);
}
@@ -189,7 +184,8 @@ public:
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
basic_any(Type &&value)
: instance{},
vtable{&basic_vtable<std::decay_t<Type>>}
vtable{&basic_vtable<std::decay_t<Type>>},
mode{policy::OWNED}
{
initialize<std::decay_t<Type>>(std::forward<Type>(value));
}
@@ -200,7 +196,8 @@ public:
*/
basic_any(const basic_any &other)
: instance{},
vtable{&basic_vtable<void>}
vtable{&basic_vtable<void>},
mode{policy::OWNED}
{
other.vtable(operation::COPY, other, this);
}
@@ -211,7 +208,8 @@ public:
*/
basic_any(basic_any &&other) ENTT_NOEXCEPT
: instance{},
vtable{other.vtable}
vtable{other.vtable},
mode{other.mode}
{
vtable(operation::MOVE, other, this);
}
@@ -227,7 +225,7 @@ public:
* @return This any object.
*/
basic_any & operator=(const basic_any &other) {
std::exchange(vtable, &basic_vtable<void>)(operation::DTOR, *this, nullptr);
reset();
other.vtable(operation::COPY, other, this);
return *this;
}
@@ -239,7 +237,8 @@ public:
*/
basic_any & operator=(basic_any &&other) {
std::exchange(vtable, other.vtable)(operation::DTOR, *this, nullptr);
vtable(operation::MOVE, other, this);
other.vtable(operation::MOVE, other, this);
mode = other.mode;
return *this;
}
@@ -300,13 +299,15 @@ public:
*/
template<typename Type, typename... Args>
void emplace(Args &&... args) {
std::exchange(vtable, &basic_vtable<Type>)(operation::DTOR, *this, nullptr);
std::exchange(vtable, &basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>)(operation::DTOR, *this, nullptr);
mode = type_to_policy<Type>();
initialize<Type>(std::forward<Args>(args)...);
}
/*! @brief Destroys contained object */
void reset() {
std::exchange(vtable, &basic_vtable<void>)(operation::DTOR, *this, nullptr);
mode = policy::OWNED;
}
/**
@@ -332,23 +333,18 @@ public:
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
basic_any ref{};
ref.instance = vtable(operation::CADDR, *this, nullptr);
vtable(operation::REF, *this, &ref);
return ref;
return basic_any{*this, (mode == policy::CREF ? policy::CREF : policy::REF)};
}
/*! @copydoc as_ref */
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
basic_any ref{};
ref.instance = vtable(operation::CADDR, *this, nullptr);
vtable(operation::CREF, *this, &ref);
return ref;
return basic_any{*this, policy::CREF};
}
private:
union { const void *instance; storage_type storage; };
vtable_type *vtable;
policy mode;
};