EnTT 3.13.0
Loading...
Searching...
No Matches
any.hpp
1#ifndef ENTT_CORE_ANY_HPP
2#define ENTT_CORE_ANY_HPP
3
4#include <cstddef>
5#include <memory>
6#include <type_traits>
7#include <utility>
8#include "../config/config.h"
9#include "../core/utility.hpp"
10#include "fwd.hpp"
11#include "type_info.hpp"
12#include "type_traits.hpp"
13
14namespace entt {
15
17namespace internal {
18
19enum class any_operation : std::uint8_t {
20 copy,
21 move,
22 transfer,
23 assign,
24 destroy,
25 compare,
26 get
27};
28
29} // namespace internal
33enum class any_policy : std::uint8_t {
35 owner,
37 ref,
39 cref
40};
41
47template<std::size_t Len, std::size_t Align>
48class basic_any {
49 using operation = internal::any_operation;
50 using vtable_type = const void *(const operation, const basic_any &, const void *);
51
52 struct storage_type {
53 alignas(Align) std::byte data[Len + !Len];
54 };
55
56 template<typename Type>
57 static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
58
59 template<typename Type>
60 static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
61 static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
62 const Type *element = nullptr;
63
64 if constexpr(in_situ<Type>) {
65 element = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
66 } else {
67 element = static_cast<const Type *>(value.instance);
68 }
69
70 switch(op) {
71 case operation::copy:
72 if constexpr(std::is_copy_constructible_v<Type>) {
73 static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
74 }
75 break;
76 case operation::move:
77 if constexpr(in_situ<Type>) {
78 if(value.mode == any_policy::owner) {
79 return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
80 }
81 }
82
83 return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
84 case operation::transfer:
85 if constexpr(std::is_move_assignable_v<Type>) {
86 *const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
87 return other;
88 }
89 [[fallthrough]];
90 case operation::assign:
91 if constexpr(std::is_copy_assignable_v<Type>) {
92 *const_cast<Type *>(element) = *static_cast<const Type *>(other);
93 return other;
94 }
95 break;
96 case operation::destroy:
97 if constexpr(in_situ<Type>) {
98 element->~Type();
99 } else if constexpr(std::is_array_v<Type>) {
100 delete[] element;
101 } else {
102 delete element;
103 }
104 break;
105 case operation::compare:
106 if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
107 return *element == *static_cast<const Type *>(other) ? other : nullptr;
108 } else {
109 return (element == other) ? other : nullptr;
110 }
111 case operation::get:
112 return element;
113 }
114
115 return nullptr;
116 }
117
118 template<typename Type, typename... Args>
119 void initialize([[maybe_unused]] Args &&...args) {
120 info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
121
122 if constexpr(!std::is_void_v<Type>) {
123 vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
124
125 if constexpr(std::is_lvalue_reference_v<Type>) {
126 static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
127 mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
128 instance = (std::addressof(args), ...);
129 } else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
130 if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
131 new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
132 } else {
133 new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
134 }
135 } else {
136 if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
137 instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
138 } else {
139 instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
140 }
141 }
142 }
143 }
144
145 basic_any(const basic_any &other, const any_policy pol) noexcept
146 : instance{other.data()},
147 info{other.info},
148 vtable{other.vtable},
149 mode{pol} {}
150
151public:
153 static constexpr auto length = Len;
155 static constexpr auto alignment = Align;
156
158 constexpr basic_any() noexcept
159 : basic_any{std::in_place_type<void>} {}
160
167 template<typename Type, typename... Args>
168 explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
169 : instance{},
170 info{},
171 vtable{},
172 mode{any_policy::owner} {
173 initialize<Type>(std::forward<Args>(args)...);
174 }
175
181 template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
182 basic_any(Type &&value)
183 : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
184
189 basic_any(const basic_any &other)
190 : basic_any{} {
191 if(other.vtable) {
192 other.vtable(operation::copy, other, this);
193 }
194 }
195
200 basic_any(basic_any &&other) noexcept
201 : instance{},
202 info{other.info},
203 vtable{other.vtable},
204 mode{other.mode} {
205 if(other.vtable) {
206 other.vtable(operation::move, other, this);
207 }
208 }
209
212 if(vtable && (mode == any_policy::owner)) {
213 vtable(operation::destroy, *this, nullptr);
214 }
215 }
216
223 reset();
224
225 if(other.vtable) {
226 other.vtable(operation::copy, other, this);
227 }
228
229 return *this;
230 }
231
237 basic_any &operator=(basic_any &&other) noexcept {
238 reset();
239
240 if(other.vtable) {
241 other.vtable(operation::move, other, this);
242 info = other.info;
243 vtable = other.vtable;
244 mode = other.mode;
245 }
246
247 return *this;
248 }
249
256 template<typename Type>
257 std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
258 operator=(Type &&value) {
259 emplace<std::decay_t<Type>>(std::forward<Type>(value));
260 return *this;
261 }
262
267 [[nodiscard]] const type_info &type() const noexcept {
268 return *info;
269 }
270
275 [[nodiscard]] const void *data() const noexcept {
276 return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
277 }
278
284 [[nodiscard]] const void *data(const type_info &req) const noexcept {
285 return *info == req ? data() : nullptr;
286 }
287
292 [[nodiscard]] void *data() noexcept {
293 return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
294 }
295
301 [[nodiscard]] void *data(const type_info &req) noexcept {
302 return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
303 }
304
311 template<typename Type, typename... Args>
312 void emplace(Args &&...args) {
313 reset();
314 initialize<Type>(std::forward<Args>(args)...);
315 }
316
322 bool assign(const basic_any &other) {
323 if(vtable && mode != any_policy::cref && *info == *other.info) {
324 return (vtable(operation::assign, *this, other.data()) != nullptr);
325 }
326
327 return false;
328 }
329
331 bool assign(basic_any &&other) {
332 if(vtable && mode != any_policy::cref && *info == *other.info) {
333 if(auto *val = other.data(); val) {
334 return (vtable(operation::transfer, *this, val) != nullptr);
335 } else {
336 return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
337 }
338 }
339
340 return false;
341 }
342
344 void reset() {
345 if(vtable && (mode == any_policy::owner)) {
346 vtable(operation::destroy, *this, nullptr);
347 }
348
349 // unnecessary but it helps to detect nasty bugs
350 ENTT_ASSERT((instance = nullptr) == nullptr, "");
351 info = &type_id<void>();
352 vtable = nullptr;
353 mode = any_policy::owner;
354 }
355
360 [[nodiscard]] explicit operator bool() const noexcept {
361 return vtable != nullptr;
362 }
363
369 [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
370 if(vtable && *info == *other.info) {
371 return (vtable(operation::compare, *this, other.data()) != nullptr);
372 }
373
374 return (!vtable && !other.vtable);
375 }
376
382 [[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
383 return !(*this == other);
384 }
385
390 [[nodiscard]] basic_any as_ref() noexcept {
391 return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
392 }
393
395 [[nodiscard]] basic_any as_ref() const noexcept {
396 return basic_any{*this, any_policy::cref};
397 }
398
403 [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
404 return (mode == any_policy::owner);
405 }
406
411 [[nodiscard]] any_policy policy() const noexcept {
412 return mode;
413 }
414
415private:
416 union {
417 const void *instance;
419 };
420 const type_info *info;
421 vtable_type *vtable;
422 any_policy mode;
423};
424
433template<typename Type, std::size_t Len, std::size_t Align>
434[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
435 const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
436 ENTT_ASSERT(instance, "Invalid instance");
437 return static_cast<Type>(*instance);
438}
439
441template<typename Type, std::size_t Len, std::size_t Align>
442[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
443 // forces const on non-reference types to make them work also with wrappers for const references
444 auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
445 ENTT_ASSERT(instance, "Invalid instance");
446 return static_cast<Type>(*instance);
447}
448
450template<typename Type, std::size_t Len, std::size_t Align>
451[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
452 if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
453 if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
454 return static_cast<Type>(std::move(*instance));
455 } else {
456 return any_cast<Type>(data);
457 }
458 } else {
459 auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
460 ENTT_ASSERT(instance, "Invalid instance");
461 return static_cast<Type>(std::move(*instance));
462 }
463}
464
466template<typename Type, std::size_t Len, std::size_t Align>
467[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
468 const auto &info = type_id<std::remove_cv_t<Type>>();
469 return static_cast<const Type *>(data->data(info));
470}
471
473template<typename Type, std::size_t Len, std::size_t Align>
474[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
475 if constexpr(std::is_const_v<Type>) {
476 // last attempt to make wrappers for const references return their values
477 return any_cast<Type>(&std::as_const(*data));
478 } else {
479 const auto &info = type_id<std::remove_cv_t<Type>>();
480 return static_cast<Type *>(data->data(info));
481 }
482}
483
493template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
494[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
495 return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
496}
497
506template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
507[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
508 return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
509}
510
511} // namespace entt
512
513#endif
A SBO friendly, type-safe container for single values of any type.
Definition any.hpp:48
void reset()
Destroys contained object.
Definition any.hpp:344
constexpr basic_any() noexcept
Default constructor.
Definition any.hpp:158
std::enable_if_t<!std::is_same_v< std::decay_t< Type >, basic_any >, basic_any & > operator=(Type &&value)
Value assignment operator.
Definition any.hpp:258
basic_any(const basic_any &other)
Copy constructor.
Definition any.hpp:189
basic_any(std::in_place_type_t< Type >, Args &&...args)
Constructs a wrapper by directly initializing the new object.
Definition any.hpp:168
const void * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:275
bool owner() const noexcept
Returns true if a wrapper owns its object, false otherwise.
Definition any.hpp:403
bool assign(const basic_any &other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:322
bool assign(basic_any &&other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:331
static constexpr auto length
Size of the internal storage.
Definition any.hpp:153
void emplace(Args &&...args)
Replaces the contained object by creating a new instance directly.
Definition any.hpp:312
const type_info & type() const noexcept
Returns the object type if any, type_id<void>() otherwise.
Definition any.hpp:267
bool operator!=(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:382
static constexpr auto alignment
Alignment requirement.
Definition any.hpp:155
basic_any(basic_any &&other) noexcept
Move constructor.
Definition any.hpp:200
any_policy policy() const noexcept
Returns the current mode of an any object.
Definition any.hpp:411
basic_any as_ref() noexcept
Aliasing constructor.
Definition any.hpp:390
~basic_any()
Frees the internal storage, whatever it means.
Definition any.hpp:211
basic_any & operator=(const basic_any &other)
Copy assignment operator.
Definition any.hpp:222
void * data(const type_info &req) noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:301
basic_any as_ref() const noexcept
Aliasing constructor.
Definition any.hpp:395
basic_any & operator=(basic_any &&other) noexcept
Move assignment operator.
Definition any.hpp:237
bool operator==(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:369
void * data() noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:292
basic_any(Type &&value)
Constructs a wrapper from a given value.
Definition any.hpp:182
const void * data(const type_info &req) const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:284
Basic storage implementation.
Definition storage.hpp:229
EnTT default namespace.
Definition dense_map.hpp:21
constexpr get_t< Type... > get
Variable template for lists of observed components.
Definition fwd.hpp:157
Type any_cast(const basic_any< Len, Align > &data) noexcept
Performs type-safe access to the contained object.
Definition any.hpp:434
basic_any< Len, Align > make_any(Args &&...args)
Constructs a wrapper from a given type, passing it all arguments.
Definition any.hpp:494
any_policy
Possible modes of an any object.
Definition any.hpp:33
@ ref
Aliasing mode, the object points to a non-const element.
@ cref
Const aliasing mode, the object points to a const element.
@ owner
Default mode, the object owns the contained element.
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition any.hpp:507
Provides a common way to define storage types.
Definition fwd.hpp:216
Implementation specific information about a type.