EnTT 4.0.0
Loading...
Searching...
No Matches
any.hpp
1#ifndef ENTT_CORE_ANY_HPP
2#define ENTT_CORE_ANY_HPP
3
4#include "../config/config.h"
5#include "../core/concepts.hpp"
6#include "../stl/concepts.hpp"
7#include "../stl/cstddef.hpp"
8#include "../stl/cstdint.hpp"
9#include "../stl/memory.hpp"
10#include "../stl/type_traits.hpp"
11#include "../stl/utility.hpp"
12#include "fwd.hpp"
13#include "type_info.hpp"
14#include "type_traits.hpp"
15#include "utility.hpp"
16
17namespace entt {
18
20namespace internal {
21
22enum class any_request : stl::uint8_t {
23 info,
24 transfer,
25 assign,
26 compare,
27 copy,
28 move
29};
30
31template<stl::size_t Len, stl::size_t Align>
32struct basic_any_storage {
33 static constexpr bool has_buffer = true;
34 union {
35 const void *instance{};
36 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
37 alignas(Align) stl::byte buffer[Len];
38 };
39};
40
41template<stl::size_t Align>
42struct basic_any_storage<0u, Align> {
43 static constexpr bool has_buffer = false;
44 const void *instance{};
45};
46
47template<typename Type, stl::size_t Len, stl::size_t Align>
48// NOLINTNEXTLINE(bugprone-sizeof-expression)
49struct in_situ: stl::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && stl::is_nothrow_move_constructible_v<Type>> {};
50
51template<stl::size_t Len, stl::size_t Align>
52struct in_situ<void, Len, Align>: stl::false_type {};
53
54} // namespace internal
56
62template<stl::size_t Len, stl::size_t Align>
63class basic_any: private internal::basic_any_storage<Len, Align> {
64 using request = internal::any_request;
65 using base_type = internal::basic_any_storage<Len, Align>;
66 using vtable_type = const void *(const request, const basic_any &, const void *);
67 using deleter_type = void(const basic_any &);
68
69 template<typename Type>
70 static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
71
72 template<cvref_unqualified Type>
73 static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
74 switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
75 using enum internal::any_request;
76 case info:
77 return &type_id<Type>();
78 case transfer:
79 if constexpr(stl::is_move_assignable_v<Type>) {
80 // NOLINTNEXTLINE(bugprone-casting-through-void)
81 *const_cast<Type *>(elem) = stl::move(*static_cast<Type *>(const_cast<void *>(other)));
82 return other;
83 }
84 [[fallthrough]];
85 case assign:
86 if constexpr(stl::is_copy_assignable_v<Type>) {
87 *const_cast<Type *>(elem) = *static_cast<const Type *>(other);
88 return other;
89 }
90 break;
91 case compare:
92 if constexpr(!stl::is_function_v<Type> && !stl::is_array_v<Type> && is_equality_comparable_v<Type>) {
93 return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
94 } else {
95 return (elem == other) ? other : nullptr;
96 }
97 case copy:
98 if constexpr(stl::is_copy_constructible_v<Type>) {
99 // NOLINTNEXTLINE(bugprone-casting-through-void)
100 static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
101 }
102 break;
103 case move:
104 ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
105 if constexpr(in_situ_v<Type>) {
106 // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
107 return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{stl::move(*const_cast<Type *>(elem))};
108 }
109 }
110
111 return nullptr;
112 }
113
114 template<cvref_unqualified Type>
115 static void basic_deleter(const basic_any &value) {
116 ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !stl::is_trivially_destructible_v<Type>), "Unexpected policy");
117
118 const auto *elem = static_cast<const Type *>(value.data());
119
120 if constexpr(in_situ_v<Type>) {
121 (value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
122 } else if constexpr(stl::is_array_v<Type>) {
123 delete[] elem;
124 } else {
125 delete elem;
126 }
127 }
128
129 template<typename Type, typename... Args>
130 void initialize([[maybe_unused]] Args &&...args) {
131 using plain_type = stl::remove_cvref_t<Type>;
132
133 vtable = basic_vtable<plain_type>;
134 underlying_type = type_hash<plain_type>::value();
135
136 if constexpr(stl::is_void_v<Type>) {
137 deleter = nullptr;
138 mode = any_policy::empty;
139 this->instance = nullptr;
140 } else if constexpr(stl::is_lvalue_reference_v<Type>) {
141 deleter = nullptr;
142 mode = stl::is_const_v<stl::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
143 static_assert((stl::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
144 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
145 this->instance = (stl::addressof(args), ...);
146 } else if constexpr(in_situ_v<plain_type>) {
147 if constexpr(stl::is_trivially_destructible_v<plain_type>) {
148 deleter = nullptr;
149 } else {
150 deleter = &basic_deleter<plain_type>;
151 }
152
153 mode = any_policy::embedded;
154
155 if constexpr(stl::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !stl::is_default_constructible_v<plain_type>)) {
156 ::new(&this->buffer) plain_type{stl::forward<Args>(args)...};
157 } else {
158 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
159 ::new(&this->buffer) plain_type(stl::forward<Args>(args)...);
160 }
161 } else {
162 deleter = &basic_deleter<plain_type>;
163 mode = any_policy::dynamic;
164
165 if constexpr(stl::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !stl::is_default_constructible_v<plain_type>)) {
166 this->instance = new plain_type{stl::forward<Args>(args)...};
167 } else if constexpr(stl::is_array_v<plain_type>) {
168 static_assert(sizeof...(Args) == 0u, "Invalid arguments");
169 this->instance = new plain_type[stl::extent_v<plain_type>]();
170 } else {
171 this->instance = new plain_type(stl::forward<Args>(args)...);
172 }
173 }
174 }
175
176 void invoke_deleter_if_exists() {
177 if(deleter != nullptr) {
178 deleter(*this);
179 }
180 }
181
182public:
184 static constexpr auto length = Len;
186 static constexpr auto alignment = Align;
187
189 constexpr basic_any() noexcept
190 : basic_any{stl::in_place_type<void>} {}
191
198 template<typename Type, typename... Args>
199 explicit basic_any(stl::in_place_type_t<Type>, Args &&...args)
200 : base_type{} {
201 initialize<Type>(stl::forward<Args>(args)...);
202 }
203
209 template<typename Type>
210 requires (!stl::is_const_v<Type> && !stl::is_void_v<Type>)
211 explicit basic_any(stl::in_place_t, Type *value)
212 : base_type{} {
213 if(value == nullptr) {
214 initialize<void>();
215 } else {
216 initialize<Type &>(*value);
217 deleter = &basic_deleter<Type>;
218 mode = any_policy::dynamic;
219 }
220 }
221
227 template<typename Type>
228 requires (!stl::same_as<stl::remove_cvref_t<Type>, basic_any>)
229 basic_any(Type &&value)
230 : basic_any{stl::in_place_type<stl::decay_t<Type>>, stl::forward<Type>(value)} {}
231
236 basic_any(const basic_any &other)
237 : basic_any{} {
238 other.vtable(request::copy, other, this);
239 }
240
245 basic_any(basic_any &&other) noexcept
246 : base_type{},
247 vtable{other.vtable},
248 deleter{other.deleter},
249 underlying_type{other.underlying_type},
250 mode{other.mode} {
251 if(other.mode == any_policy::embedded) {
252 other.vtable(request::move, other, this);
253 } else if(other.mode != any_policy::empty) {
254 this->instance = stl::exchange(other.instance, nullptr);
255 }
256 }
257
260 invoke_deleter_if_exists();
261 }
262
269 if(this != &other) {
270 invoke_deleter_if_exists();
271
272 if(other) {
273 other.vtable(request::copy, other, this);
274 } else {
275 initialize<void>();
276 }
277 }
278
279 return *this;
280 }
281
287 basic_any &operator=(basic_any &&other) noexcept {
288 if(this != &other) {
289 invoke_deleter_if_exists();
290
291 if(other.mode == any_policy::embedded) {
292 other.vtable(request::move, other, this);
293 } else if(other.mode != any_policy::empty) {
294 this->instance = stl::exchange(other.instance, nullptr);
295 }
296
297 vtable = other.vtable;
298 deleter = other.deleter;
299 underlying_type = other.underlying_type;
300 mode = other.mode;
301 }
302
303 return *this;
304 }
305
312 template<typename Type>
313 requires (!stl::same_as<stl::remove_cvref_t<Type>, basic_any>)
314 basic_any &operator=(Type &&value) {
315 emplace<stl::decay_t<Type>>(stl::forward<Type>(value));
316 return *this;
317 }
318
323 [[nodiscard]] bool has_value() const noexcept {
324 return (mode != any_policy::empty);
325 }
326
334 [[nodiscard]] bool has_value(const type_info &req) const noexcept {
335 return (underlying_type == req.hash());
336 }
337
345 template<cvref_unqualified Type>
346 [[nodiscard]] bool has_value() const noexcept {
347 return (underlying_type == type_hash<Type>::value());
348 }
349
354 [[nodiscard]] const type_info &info() const noexcept {
355 return *static_cast<const type_info *>(vtable(request::info, *this, nullptr));
356 }
357
362 [[nodiscard]] const void *data() const noexcept {
363 if constexpr(base_type::has_buffer) {
364 return (mode == any_policy::embedded) ? &this->buffer : this->instance;
365 } else {
366 return this->instance;
367 }
368 }
369
375 [[nodiscard]] const void *data(const type_info &req) const noexcept {
376 return has_value(req) ? data() : nullptr;
377 }
378
384 template<typename Type>
385 [[nodiscard]] const Type *data() const noexcept {
386 return has_value<stl::remove_const_t<Type>>() ? static_cast<const Type *>(data()) : nullptr;
387 }
388
393 [[nodiscard]] void *data() noexcept {
394 return (mode == any_policy::cref) ? nullptr : const_cast<void *>(stl::as_const(*this).data());
395 }
396
402 [[nodiscard]] void *data(const type_info &req) noexcept {
403 return (mode == any_policy::cref) ? nullptr : const_cast<void *>(stl::as_const(*this).data(req));
404 }
405
411 template<typename Type>
412 [[nodiscard]] Type *data() noexcept {
413 if constexpr(stl::is_const_v<Type>) {
414 return stl::as_const(*this).template data<stl::remove_const_t<Type>>();
415 } else {
416 return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(stl::as_const(*this).template data<stl::remove_const_t<Type>>());
417 }
418 }
419
426 template<typename Type, typename... Args>
427 void emplace(Args &&...args) {
428 invoke_deleter_if_exists();
429 initialize<Type>(stl::forward<Args>(args)...);
430 }
431
437 bool assign(const basic_any &other) {
438 if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
439 return (vtable(request::assign, *this, other.data()) != nullptr);
440 }
441
442 return false;
443 }
444
446 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
447 bool assign(basic_any &&other) {
448 if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
449 return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, stl::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr);
450 }
451
452 return false;
453 }
454
456 void reset() {
457 invoke_deleter_if_exists();
458 initialize<void>();
459 }
460
465 [[nodiscard]] explicit operator bool() const noexcept {
466 return has_value();
467 }
468
474 [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
475 if(other && (underlying_type == other.underlying_type)) {
476 return (vtable(request::compare, *this, other.data()) != nullptr);
477 }
478
479 return (!*this && !other);
480 }
481
486 [[nodiscard]] basic_any as_ref() noexcept {
487 basic_any other = stl::as_const(*this).as_ref();
488
489 switch(mode) {
490 using enum any_policy;
491 case cref:
492 case empty:
493 other.mode = mode;
494 break;
495 default:
496 other.mode = any_policy::ref;
497 break;
498 }
499
500 return other;
501 }
502
504 [[nodiscard]] basic_any as_ref() const noexcept {
505 basic_any other{};
506 other.instance = data();
507 other.vtable = vtable;
508 other.underlying_type = underlying_type;
509 other.mode = any_policy::cref;
510 return other;
511 }
512
517 [[nodiscard]] bool owner() const noexcept {
518 return (mode == any_policy::dynamic || mode == any_policy::embedded);
519 }
520
525 [[nodiscard]] any_policy policy() const noexcept {
526 return mode;
527 }
528
529private:
530 vtable_type *vtable{};
531 deleter_type *deleter{};
532 id_type underlying_type{};
533 any_policy mode{};
534};
535
544template<typename Type, stl::size_t Len, stl::size_t Align>
545[[nodiscard]] stl::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
546 const auto *const instance = any_cast<stl::remove_reference_t<Type>>(&data);
547 ENTT_ASSERT(instance, "Invalid instance");
548 return static_cast<Type>(*instance);
549}
550
552template<typename Type, stl::size_t Len, stl::size_t Align>
553[[nodiscard]] stl::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
554 // forces const on non-reference types to make them work also with wrappers for const references
555 auto *const instance = any_cast<stl::remove_reference_t<const Type>>(&data);
556 ENTT_ASSERT(instance, "Invalid instance");
557 return static_cast<Type>(*instance);
558}
559
561template<typename Type, stl::size_t Len, stl::size_t Align>
562// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
563[[nodiscard]] stl::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
564 if constexpr(stl::is_copy_constructible_v<stl::remove_cvref_t<Type>>) {
565 if(auto *const instance = any_cast<stl::remove_reference_t<Type>>(&data); instance) {
566 return static_cast<Type>(stl::move(*instance));
567 }
568
569 return any_cast<Type>(data);
570 } else {
571 auto *const instance = any_cast<stl::remove_reference_t<Type>>(&data);
572 ENTT_ASSERT(instance, "Invalid instance");
573 return static_cast<Type>(stl::move(*instance));
574 }
575}
576
578template<typename Type, stl::size_t Len, stl::size_t Align>
579[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
580 return data->template data<stl::remove_const_t<Type>>();
581}
582
584template<typename Type, stl::size_t Len, stl::size_t Align>
585[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
586 if constexpr(stl::is_const_v<Type>) {
587 // last attempt to make wrappers for const references return their values
588 return any_cast<Type>(&stl::as_const(*data));
589 } else {
590 return data->template data<Type>();
591 }
592}
593
603template<typename Type, stl::size_t Len = basic_any<>::length, stl::size_t Align = basic_any<Len>::alignment, typename... Args>
604[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
605 return basic_any<Len, Align>{stl::in_place_type<Type>, stl::forward<Args>(args)...};
606}
607
616template<stl::size_t Len = basic_any<>::length, stl::size_t Align = basic_any<Len>::alignment, typename Type>
617[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
618 return basic_any<Len, Align>{stl::in_place_type<Type &&>, stl::forward<Type>(value)};
619}
620
621} // namespace entt
622
623#endif
A SBO friendly, type-safe container for single values of any type.
Definition any.hpp:63
void reset()
Destroys contained object.
Definition any.hpp:456
constexpr basic_any() noexcept
Default constructor.
Definition any.hpp:189
basic_any(const basic_any &other)
Copy constructor.
Definition any.hpp:236
const Type * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:385
const void * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:362
bool has_value() const noexcept
Returns false if a wrapper is empty, true otherwise.
Definition any.hpp:323
bool owner() const noexcept
Returns true if a wrapper owns its object, false otherwise.
Definition any.hpp:517
bool assign(const basic_any &other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:437
bool assign(basic_any &&other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:447
static constexpr auto length
Definition any.hpp:184
void emplace(Args &&...args)
Definition any.hpp:427
static constexpr auto alignment
Definition any.hpp:186
const type_info & info() const noexcept
Returns the object type info if any, type_id<void>() otherwise.
Definition any.hpp:354
basic_any(basic_any &&other) noexcept
Move constructor.
Definition any.hpp:245
any_policy policy() const noexcept
Returns the current mode of an any object.
Definition any.hpp:525
basic_any(stl::in_place_type_t< Type >, Args &&...args)
Constructs a wrapper by directly initializing the new object.
Definition any.hpp:199
basic_any as_ref() noexcept
Aliasing constructor.
Definition any.hpp:486
basic_any(Type &&value)
Constructs a wrapper from a given value.
Definition any.hpp:229
~basic_any()
Frees the internal buffer, whatever it means.
Definition any.hpp:259
basic_any & operator=(const basic_any &other)
Copy assignment operator.
Definition any.hpp:268
void * data(const type_info &req) noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:402
basic_any as_ref() const noexcept
Aliasing constructor.
Definition any.hpp:504
bool has_value() const noexcept
Returns false if the wrapper does not contain the expected type, true otherwise.
Definition any.hpp:346
basic_any & operator=(basic_any &&other) noexcept
Move assignment operator.
Definition any.hpp:287
bool has_value(const type_info &req) const noexcept
Returns false if the wrapper does not contain the expected type, true otherwise.
Definition any.hpp:334
bool operator==(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:474
void * data() noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:393
Type * data() noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:412
const void * data(const type_info &req) const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:375
basic_any(stl::in_place_t, Type *value)
Constructs a wrapper taking ownership of the passed object.
Definition any.hpp:211
EnTT default namespace.
Definition dense_map.hpp:25
stl::remove_const_t< Type > any_cast(const basic_any< Len, Align > &data) noexcept
Performs type-safe access to the contained object.
Definition any.hpp:545
any_policy
Possible modes of an any object.
Definition fwd.hpp:11
@ ref
Aliasing mode, non-const reference.
Definition fwd.hpp:19
@ cref
Const aliasing mode, const reference.
Definition fwd.hpp:21
@ embedded
Owning mode, embedded element.
Definition fwd.hpp:17
@ empty
Default mode, no element available.
Definition fwd.hpp:13
@ dynamic
Owning mode, dynamically allocated element.
Definition fwd.hpp:15
basic_any< Len, Align > make_any(Args &&...args)
Constructs a wrapper from a given type, passing it all arguments.
Definition any.hpp:604
stl::uint32_t id_type
Alias declaration for type identifiers.
Definition fwd.hpp:29
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition any.hpp:617
static constexpr id_type value() noexcept
Returns the numeric representation of a given type.
Implementation specific information about a type.