1#ifndef ENTT_CORE_ANY_HPP
2#define ENTT_CORE_ANY_HPP
8#include "../config/config.h"
10#include "type_info.hpp"
11#include "type_traits.hpp"
19enum class any_request : std::uint8_t {
28template<std::
size_t Len, std::
size_t Align>
29struct basic_any_storage {
30 static constexpr bool has_buffer =
true;
32 const void *instance{};
34 alignas(Align) std::byte buffer[Len];
38template<std::
size_t Align>
39struct basic_any_storage<0u, Align> {
40 static constexpr bool has_buffer =
false;
41 const void *instance{};
44template<
typename Type, std::
size_t Len, std::
size_t Align>
46struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>> {};
48template<std::size_t Len, std::size_t Align>
49struct in_situ<void, Len, Align>: std::false_type {};
59template<std::size_t Len, std::size_t Align>
60class basic_any: private internal::basic_any_storage<Len, Align> {
61 using request = internal::any_request;
62 using base_type = internal::basic_any_storage<Len, Align>;
63 using vtable_type = const void *(const request, const basic_any &, const void *);
64 using deleter_type = void(const basic_any &);
66 template<typename Type>
67 static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
69 template<typename Type>
70 static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
71 static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
73 switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
75 return &type_id<Type>();
76 case request::transfer:
77 if constexpr(std::is_move_assignable_v<Type>) {
79 *const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
84 if constexpr(std::is_copy_assignable_v<Type>) {
85 *const_cast<Type *>(elem) = *static_cast<const Type *>(other);
89 case request::compare:
90 if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
91 return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
93 return (elem == other) ? other : nullptr;
96 if constexpr(std::is_copy_constructible_v<Type>) {
98 static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
102 ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
103 if constexpr(in_situ_v<Type>) {
105 return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{std::move(*
const_cast<Type *
>(elem))};
112 template<
typename Type>
113 static void basic_deleter(
const basic_any &value) {
114 static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>,
"Invalid type");
115 ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v<Type>),
"Unexpected policy");
117 const auto *elem =
static_cast<const Type *
>(value.data());
119 if constexpr(in_situ_v<Type>) {
120 (value.mode == any_policy::embedded) ? elem->~Type() : (
delete elem);
121 }
else if constexpr(std::is_array_v<Type>) {
128 template<
typename Type,
typename... Args>
129 void initialize([[maybe_unused]] Args &&...args) {
130 using plain_type = std::remove_const_t<std::remove_reference_t<Type>>;
132 vtable = basic_vtable<plain_type>;
133 underlying_type = type_hash<plain_type>::value();
135 if constexpr(std::is_void_v<Type>) {
137 mode = any_policy::empty;
138 this->instance =
nullptr;
139 }
else if constexpr(std::is_lvalue_reference_v<Type>) {
141 mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
142 static_assert((std::is_lvalue_reference_v<Args> && ...) && (
sizeof...(Args) == 1u),
"Invalid arguments");
144 this->instance = (std::addressof(args), ...);
145 }
else if constexpr(in_situ_v<plain_type>) {
146 if constexpr(std::is_trivially_destructible_v<plain_type>) {
149 deleter = &basic_deleter<plain_type>;
152 mode = any_policy::embedded;
154 if constexpr(std::is_aggregate_v<plain_type> && (
sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
155 ::new(&this->buffer) plain_type{std::forward<Args>(args)...};
158 ::new(&this->buffer) plain_type(std::forward<Args>(args)...);
161 deleter = &basic_deleter<plain_type>;
162 mode = any_policy::dynamic;
164 if constexpr(std::is_aggregate_v<plain_type> && (
sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
165 this->instance =
new plain_type{std::forward<Args>(args)...};
166 }
else if constexpr(std::is_array_v<plain_type>) {
167 static_assert(
sizeof...(Args) == 0u,
"Invalid arguments");
168 this->instance =
new plain_type[std::extent_v<plain_type>]();
170 this->instance =
new plain_type(std::forward<Args>(args)...);
175 void invoke_deleter_if_exists() {
176 if(deleter !=
nullptr) {
197 template<
typename Type,
typename... Args>
198 explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
200 initialize<Type>(std::forward<Args>(args)...);
208 template<
typename Type>
211 static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>,
"Non-const non-void pointer required");
213 if(value ==
nullptr) {
216 initialize<Type &>(*value);
217 deleter = &basic_deleter<Type>;
227 template<
typename Type,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
229 :
basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
237 other.vtable(request::copy, other,
this);
246 vtable{other.vtable},
247 deleter{other.deleter},
248 underlying_type{other.underlying_type},
250 if(other.mode == any_policy::embedded) {
251 other.vtable(request::move, other,
this);
252 }
else if(other.mode != any_policy::empty) {
253 this->instance = std::exchange(other.instance,
nullptr);
259 invoke_deleter_if_exists();
269 invoke_deleter_if_exists();
272 other.vtable(request::copy, other,
this);
288 invoke_deleter_if_exists();
291 other.vtable(request::move, other,
this);
293 this->instance = std::exchange(other.instance,
nullptr);
296 vtable = other.vtable;
297 deleter = other.deleter;
298 underlying_type = other.underlying_type;
311 template<
typename Type,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
333 return (underlying_type == req.hash());
343 template<
typename Type>
345 static_assert(std::is_same_v<std::remove_const_t<Type>, Type>,
"Invalid type");
354 return *
static_cast<const type_info *
>(vtable(request::info, *
this,
nullptr));
358 [[deprecated(
"use ::info instead")]] [[nodiscard]]
const type_info &
type() const noexcept {
366 [[nodiscard]]
const void *
data() const noexcept {
367 if constexpr(base_type::has_buffer) {
370 return this->instance;
388 template<
typename Type>
389 [[nodiscard]]
const Type *
data() const noexcept {
397 [[nodiscard]]
void *
data() noexcept {
398 return (mode ==
any_policy::cref) ? nullptr :
const_cast<void *
>(std::as_const(*this).data());
407 return (mode ==
any_policy::cref) ? nullptr :
const_cast<void *
>(std::as_const(*this).data(req));
415 template<
typename Type>
416 [[nodiscard]] Type *
data() noexcept {
417 if constexpr(std::is_const_v<Type>) {
418 return std::as_const(*this).template
data<std::remove_const_t<Type>>();
420 return (mode ==
any_policy::cref) ? nullptr :
const_cast<Type *
>(std::as_const(*this).template
data<std::remove_const_t<Type>>());
430 template<
typename Type,
typename... Args>
432 invoke_deleter_if_exists();
433 initialize<Type>(std::forward<Args>(args)...);
442 if(other && (mode !=
any_policy::cref) && (underlying_type == other.underlying_type)) {
443 return (vtable(request::assign, *
this, other.
data()) !=
nullptr);
452 if(other && (mode !=
any_policy::cref) && (underlying_type == other.underlying_type)) {
453 return (other.mode ==
any_policy::cref) ? (vtable(request::assign, *
this, std::as_const(other).
data()) !=
nullptr) : (vtable(request::transfer, *
this, other.data()) !=
nullptr);
461 invoke_deleter_if_exists();
469 [[nodiscard]]
explicit operator bool() const noexcept {
479 if(other && (underlying_type == other.underlying_type)) {
480 return (vtable(request::compare, *
this, other.data()) !=
nullptr);
483 return (!*
this && !other);
492 return !(*
this == other);
500 basic_any other = std::as_const(*this).as_ref();
508 other.instance =
data();
509 other.vtable = vtable;
510 other.underlying_type = underlying_type;
519 [[nodiscard]]
bool owner() const noexcept {
532 vtable_type *vtable{};
533 deleter_type *deleter{};
546template<
typename Type, std::
size_t Len, std::
size_t Align>
549 ENTT_ASSERT(instance,
"Invalid instance");
550 return static_cast<Type
>(*instance);
554template<
typename Type, std::
size_t Len, std::
size_t Align>
558 ENTT_ASSERT(instance,
"Invalid instance");
559 return static_cast<Type
>(*instance);
563template<
typename Type, std::
size_t Len, std::
size_t Align>
566 if constexpr(std::is_copy_constructible_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
567 if(
auto *
const instance =
any_cast<std::remove_reference_t<Type>>(&data); instance) {
568 return static_cast<Type
>(std::move(*instance));
574 ENTT_ASSERT(instance,
"Invalid instance");
575 return static_cast<Type
>(std::move(*instance));
580template<
typename Type, std::
size_t Len, std::
size_t Align>
582 return data->template data<std::remove_const_t<Type>>();
586template<
typename Type, std::
size_t Len, std::
size_t Align>
588 if constexpr(std::is_const_v<Type>) {
592 return data->template data<Type>();
605template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment,
typename... Args>
618template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment,
typename Type>
A SBO friendly, type-safe container for single values of any type.
void reset()
Destroys contained object.
constexpr basic_any() noexcept
Default constructor.
basic_any(const basic_any &other)
Copy constructor.
basic_any(std::in_place_type_t< Type >, Args &&...args)
Constructs a wrapper by directly initializing the new object.
const Type * data() const noexcept
Returns an opaque pointer to the contained instance.
const void * data() const noexcept
Returns an opaque pointer to the contained instance.
bool has_value() const noexcept
Returns false if a wrapper is empty, true otherwise.
bool owner() const noexcept
Returns true if a wrapper owns its object, false otherwise.
bool assign(const basic_any &other)
Assigns a value to the contained object without replacing it.
bool assign(basic_any &&other)
Assigns a value to the contained object without replacing it.
static constexpr auto length
void emplace(Args &&...args)
const type_info & type() const noexcept
Returns the object type info if any, type_id<void>() otherwise.
bool operator!=(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
static constexpr auto alignment
const type_info & info() const noexcept
Returns the object type info if any, type_id<void>() otherwise.
basic_any(basic_any &&other) noexcept
Move constructor.
any_policy policy() const noexcept
Returns the current mode of an any object.
basic_any(std::in_place_t, Type *value)
Constructs a wrapper taking ownership of the passed object.
basic_any as_ref() noexcept
Aliasing constructor.
~basic_any()
Frees the internal buffer, whatever it means.
basic_any & operator=(const basic_any &other)
Copy assignment operator.
void * data(const type_info &req) noexcept
Returns an opaque pointer to the contained instance.
basic_any as_ref() const noexcept
Aliasing constructor.
bool has_value() const noexcept
Returns false if the wrapper does not contain the expected type, true otherwise.
basic_any & operator=(Type &&value)
Value assignment operator.
basic_any & operator=(basic_any &&other) noexcept
Move assignment operator.
bool has_value(const type_info &req) const noexcept
Returns false if the wrapper does not contain the expected type, true otherwise.
bool operator==(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
void * data() noexcept
Returns an opaque pointer to the contained instance.
Type * data() noexcept
Returns an opaque pointer to the contained instance.
basic_any(Type &&value)
Constructs a wrapper from a given value.
const void * data(const type_info &req) const noexcept
Returns an opaque pointer to the contained instance.
std::remove_const_t< Type > any_cast(const basic_any< Len, Align > &data) noexcept
Performs type-safe access to the contained object.
std::uint32_t id_type
Alias declaration for type identifiers.
basic_any< Len, Align > make_any(Args &&...args)
Constructs a wrapper from a given type, passing it all arguments.
any_policy
Possible modes of an any object.
@ ref
Aliasing mode, non-const reference.
@ cref
Const aliasing mode, const reference.
@ embedded
Owning mode, embedded element.
@ empty
Default mode, no element available.
@ dynamic
Owning mode, dynamically allocated element.
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
static constexpr id_type value() noexcept
Returns the numeric representation of a given type.
Implementation specific information about a type.