EnTT 3.16.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 "fwd.hpp"
10#include "type_info.hpp"
11#include "type_traits.hpp"
12#include "utility.hpp"
13
14namespace entt {
15
17namespace internal {
18
19enum class any_request : std::uint8_t {
20 info,
21 transfer,
22 assign,
23 compare,
24 copy,
25 move
26};
27
28template<std::size_t Len, std::size_t Align>
29struct basic_any_storage {
30 static constexpr bool has_buffer = true;
31 union {
32 const void *instance{};
33 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
34 alignas(Align) std::byte buffer[Len];
35 };
36};
37
38template<std::size_t Align>
39struct basic_any_storage<0u, Align> {
40 static constexpr bool has_buffer = false;
41 const void *instance{};
42};
43
44template<typename Type, std::size_t Len, std::size_t Align>
45// NOLINTNEXTLINE(bugprone-sizeof-expression)
46struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>> {};
47
48template<std::size_t Len, std::size_t Align>
49struct in_situ<void, Len, Align>: std::false_type {};
50
51} // namespace internal
53
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 &);
65
66 template<typename Type>
67 static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
68
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");
72
73 switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
74 case request::info:
75 return &type_id<Type>();
76 case request::transfer:
77 if constexpr(std::is_move_assignable_v<Type>) {
78 // NOLINTNEXTLINE(bugprone-casting-through-void)
79 *const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
80 return other;
81 }
82 [[fallthrough]];
83 case request::assign:
84 if constexpr(std::is_copy_assignable_v<Type>) {
85 *const_cast<Type *>(elem) = *static_cast<const Type *>(other);
86 return other;
87 }
88 break;
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;
92 } else {
93 return (elem == other) ? other : nullptr;
94 }
95 case request::copy:
96 if constexpr(std::is_copy_constructible_v<Type>) {
97 // NOLINTNEXTLINE(bugprone-casting-through-void)
98 static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
99 }
100 break;
101 case request::move:
102 ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
103 if constexpr(in_situ_v<Type>) {
104 // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
105 return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{std::move(*const_cast<Type *>(elem))};
106 }
107 }
108
109 return nullptr;
110 }
111
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");
116
117 const auto *elem = static_cast<const Type *>(value.data());
118
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>) {
122 delete[] elem;
123 } else {
124 delete elem;
125 }
126 }
127
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>>;
131
132 vtable = basic_vtable<plain_type>;
133 underlying_type = type_hash<plain_type>::value();
134
135 if constexpr(std::is_void_v<Type>) {
136 deleter = nullptr;
137 mode = any_policy::empty;
138 this->instance = nullptr;
139 } else if constexpr(std::is_lvalue_reference_v<Type>) {
140 deleter = nullptr;
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");
143 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
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>) {
147 deleter = nullptr;
148 } else {
149 deleter = &basic_deleter<plain_type>;
150 }
151
152 mode = any_policy::embedded;
153
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)...};
156 } else {
157 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
158 ::new(&this->buffer) plain_type(std::forward<Args>(args)...);
159 }
160 } else {
161 deleter = &basic_deleter<plain_type>;
162 mode = any_policy::dynamic;
163
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>]();
169 } else {
170 this->instance = new plain_type(std::forward<Args>(args)...);
171 }
172 }
173 }
174
175 void invoke_deleter_if_exists() {
176 if(deleter != nullptr) {
177 deleter(*this);
178 }
179 }
180
181public:
183 static constexpr auto length = Len;
185 static constexpr auto alignment = Align;
186
188 constexpr basic_any() noexcept
189 : basic_any{std::in_place_type<void>} {}
190
197 template<typename Type, typename... Args>
198 explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
199 : base_type{} {
200 initialize<Type>(std::forward<Args>(args)...);
201 }
202
208 template<typename Type>
209 explicit basic_any(std::in_place_t, Type *value)
210 : base_type{} {
211 static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
212
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, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
228 basic_any(Type &&value)
229 : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
230
235 basic_any(const basic_any &other)
236 : basic_any{} {
237 other.vtable(request::copy, other, this);
238 }
239
244 basic_any(basic_any &&other) noexcept
245 : base_type{},
246 vtable{other.vtable},
247 deleter{other.deleter},
248 underlying_type{other.underlying_type},
249 mode{other.mode} {
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);
254 }
255 }
256
259 invoke_deleter_if_exists();
260 }
261
268 if(this != &other) {
269 invoke_deleter_if_exists();
270
271 if(other) {
272 other.vtable(request::copy, other, this);
273 } else {
274 initialize<void>();
275 }
276 }
277
278 return *this;
279 }
280
286 basic_any &operator=(basic_any &&other) noexcept {
287 if(this != &other) {
288 invoke_deleter_if_exists();
289
290 if(other.mode == any_policy::embedded) {
291 other.vtable(request::move, other, this);
292 } else if(other.mode != any_policy::empty) {
293 this->instance = std::exchange(other.instance, nullptr);
294 }
295
296 vtable = other.vtable;
297 deleter = other.deleter;
298 underlying_type = other.underlying_type;
299 mode = other.mode;
300 }
301
302 return *this;
303 }
304
311 template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
312 basic_any &operator=(Type &&value) {
313 emplace<std::decay_t<Type>>(std::forward<Type>(value));
314 return *this;
315 }
316
321 [[nodiscard]] bool has_value() const noexcept {
322 return (mode != any_policy::empty);
323 }
324
332 [[nodiscard]] bool has_value(const type_info &req) const noexcept {
333 return (underlying_type == req.hash());
334 }
335
343 template<typename Type>
344 [[nodiscard]] bool has_value() const noexcept {
345 static_assert(std::is_same_v<std::remove_const_t<Type>, Type>, "Invalid type");
346 return (underlying_type == type_hash<Type>::value());
347 }
348
353 [[nodiscard]] const type_info &info() const noexcept {
354 return *static_cast<const type_info *>(vtable(request::info, *this, nullptr));
355 }
356
358 [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
359 return info();
360 }
361
366 [[nodiscard]] const void *data() const noexcept {
367 if constexpr(base_type::has_buffer) {
368 return (mode == any_policy::embedded) ? &this->buffer : this->instance;
369 } else {
370 return this->instance;
371 }
372 }
373
379 [[nodiscard]] const void *data(const type_info &req) const noexcept {
380 return has_value(req) ? data() : nullptr;
381 }
382
388 template<typename Type>
389 [[nodiscard]] const Type *data() const noexcept {
390 return has_value<std::remove_const_t<Type>>() ? static_cast<const Type *>(data()) : nullptr;
391 }
392
397 [[nodiscard]] void *data() noexcept {
398 return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data());
399 }
400
406 [[nodiscard]] void *data(const type_info &req) noexcept {
407 return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
408 }
409
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>>();
419 } else {
420 return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(std::as_const(*this).template data<std::remove_const_t<Type>>());
421 }
422 }
423
430 template<typename Type, typename... Args>
431 void emplace(Args &&...args) {
432 invoke_deleter_if_exists();
433 initialize<Type>(std::forward<Args>(args)...);
434 }
435
441 bool assign(const basic_any &other) {
442 if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
443 return (vtable(request::assign, *this, other.data()) != nullptr);
444 }
445
446 return false;
447 }
448
450 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
451 bool assign(basic_any &&other) {
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);
454 }
455
456 return false;
457 }
458
460 void reset() {
461 invoke_deleter_if_exists();
462 initialize<void>();
463 }
464
469 [[nodiscard]] explicit operator bool() const noexcept {
470 return has_value();
471 }
472
478 [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
479 if(other && (underlying_type == other.underlying_type)) {
480 return (vtable(request::compare, *this, other.data()) != nullptr);
481 }
482
483 return (!*this && !other);
484 }
485
491 [[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
492 return !(*this == other);
493 }
494
499 [[nodiscard]] basic_any as_ref() noexcept {
500 basic_any other = std::as_const(*this).as_ref();
501 other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref);
502 return other;
503 }
504
506 [[nodiscard]] basic_any as_ref() const noexcept {
507 basic_any other{};
508 other.instance = data();
509 other.vtable = vtable;
510 other.underlying_type = underlying_type;
511 other.mode = any_policy::cref;
512 return other;
513 }
514
519 [[nodiscard]] bool owner() const noexcept {
520 return (mode == any_policy::dynamic || mode == any_policy::embedded);
521 }
522
527 [[nodiscard]] any_policy policy() const noexcept {
528 return mode;
529 }
530
531private:
532 vtable_type *vtable{};
533 deleter_type *deleter{};
534 id_type underlying_type{};
535 any_policy mode{};
536};
537
546template<typename Type, std::size_t Len, std::size_t Align>
547[[nodiscard]] std::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
548 const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
549 ENTT_ASSERT(instance, "Invalid instance");
550 return static_cast<Type>(*instance);
551}
552
554template<typename Type, std::size_t Len, std::size_t Align>
555[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
556 // forces const on non-reference types to make them work also with wrappers for const references
557 auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
558 ENTT_ASSERT(instance, "Invalid instance");
559 return static_cast<Type>(*instance);
560}
561
563template<typename Type, std::size_t Len, std::size_t Align>
564// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
565[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
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));
569 }
570
571 return any_cast<Type>(data);
572 } else {
573 auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
574 ENTT_ASSERT(instance, "Invalid instance");
575 return static_cast<Type>(std::move(*instance));
576 }
577}
578
580template<typename Type, std::size_t Len, std::size_t Align>
581[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
582 return data->template data<std::remove_const_t<Type>>();
583}
584
586template<typename Type, std::size_t Len, std::size_t Align>
587[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
588 if constexpr(std::is_const_v<Type>) {
589 // last attempt to make wrappers for const references return their values
590 return any_cast<Type>(&std::as_const(*data));
591 } else {
592 return data->template data<Type>();
593 }
594}
595
605template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
606[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
607 return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
608}
609
618template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
619[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
620 return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
621}
622
623} // namespace entt
624
625#endif
A SBO friendly, type-safe container for single values of any type.
Definition any.hpp:60
void reset()
Destroys contained object.
Definition any.hpp:460
constexpr basic_any() noexcept
Default constructor.
Definition any.hpp:188
basic_any(const basic_any &other)
Copy constructor.
Definition any.hpp:235
basic_any(std::in_place_type_t< Type >, Args &&...args)
Constructs a wrapper by directly initializing the new object.
Definition any.hpp:198
const Type * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:389
const void * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:366
bool has_value() const noexcept
Returns false if a wrapper is empty, true otherwise.
Definition any.hpp:321
bool owner() const noexcept
Returns true if a wrapper owns its object, false otherwise.
Definition any.hpp:519
bool assign(const basic_any &other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:441
bool assign(basic_any &&other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:451
static constexpr auto length
Definition any.hpp:183
void emplace(Args &&...args)
Definition any.hpp:431
const type_info & type() const noexcept
Returns the object type info if any, type_id<void>() otherwise.
Definition any.hpp:358
bool operator!=(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:491
static constexpr auto alignment
Definition any.hpp:185
const type_info & info() const noexcept
Returns the object type info if any, type_id<void>() otherwise.
Definition any.hpp:353
basic_any(basic_any &&other) noexcept
Move constructor.
Definition any.hpp:244
any_policy policy() const noexcept
Returns the current mode of an any object.
Definition any.hpp:527
basic_any(std::in_place_t, Type *value)
Constructs a wrapper taking ownership of the passed object.
Definition any.hpp:209
basic_any as_ref() noexcept
Aliasing constructor.
Definition any.hpp:499
~basic_any()
Frees the internal buffer, whatever it means.
Definition any.hpp:258
basic_any & operator=(const basic_any &other)
Copy assignment operator.
Definition any.hpp:267
void * data(const type_info &req) noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:406
basic_any as_ref() const noexcept
Aliasing constructor.
Definition any.hpp:506
bool has_value() const noexcept
Returns false if the wrapper does not contain the expected type, true otherwise.
Definition any.hpp:344
basic_any & operator=(Type &&value)
Value assignment operator.
Definition any.hpp:312
basic_any & operator=(basic_any &&other) noexcept
Move assignment operator.
Definition any.hpp:286
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:332
bool operator==(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:478
void * data() noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:397
Type * data() noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:416
basic_any(Type &&value)
Constructs a wrapper from a given value.
Definition any.hpp:228
const void * data(const type_info &req) const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:379
EnTT default namespace.
Definition dense_map.hpp:22
std::remove_const_t< Type > any_cast(const basic_any< Len, Align > &data) noexcept
Performs type-safe access to the contained object.
Definition any.hpp:547
std::uint32_t id_type
Alias declaration for type identifiers.
Definition fwd.hpp:29
basic_any< Len, Align > make_any(Args &&...args)
Constructs a wrapper from a given type, passing it all arguments.
Definition any.hpp:606
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 > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition any.hpp:619
static constexpr id_type value() noexcept
Returns the numeric representation of a given type.
Implementation specific information about a type.