EnTT 3.15.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_request : std::uint8_t {
20 transfer,
21 assign,
22 destroy,
23 compare,
24 copy,
25 move,
26 get
27};
28
29} // namespace internal
31
37template<std::size_t Len, std::size_t Align>
38class basic_any {
39 using request = internal::any_request;
40 using vtable_type = const void *(const request, const basic_any &, const void *);
41
42 struct storage_type {
43 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
44 alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
45 };
46
47 template<typename Type>
48 // NOLINTNEXTLINE(bugprone-sizeof-expression)
49 static constexpr bool in_situ = (Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
50
51 template<typename Type>
52 static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
53 static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
54 const Type *elem = nullptr;
55
56 if constexpr(in_situ<Type>) {
57 elem = (value.mode == any_policy::embedded) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
58 } else {
59 elem = static_cast<const Type *>(value.instance);
60 }
61
62 switch(req) {
63 case request::transfer:
64 if constexpr(std::is_move_assignable_v<Type>) {
65 // NOLINTNEXTLINE(bugprone-casting-through-void)
66 *const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
67 return other;
68 }
69 [[fallthrough]];
70 case request::assign:
71 if constexpr(std::is_copy_assignable_v<Type>) {
72 *const_cast<Type *>(elem) = *static_cast<const Type *>(other);
73 return other;
74 }
75 break;
76 case request::destroy:
77 if constexpr(in_situ<Type>) {
78 (value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
79 } else if constexpr(std::is_array_v<Type>) {
80 delete[] elem;
81 } else {
82 delete elem;
83 }
84 break;
85 case request::compare:
86 if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
87 return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
88 } else {
89 return (elem == other) ? other : nullptr;
90 }
91 case request::copy:
92 if constexpr(std::is_copy_constructible_v<Type>) {
93 // NOLINTNEXTLINE(bugprone-casting-through-void)
94 static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
95 }
96 break;
97 case request::move:
98 ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
99 if constexpr(in_situ<Type>) {
100 // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
101 return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))};
102 }
103 [[fallthrough]];
104 case request::get:
105 ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
106 if constexpr(in_situ<Type>) {
107 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
108 return elem;
109 }
110 }
111
112 return nullptr;
113 }
114
115 template<typename Type, typename... Args>
116 void initialize([[maybe_unused]] Args &&...args) {
117 if constexpr(!std::is_void_v<Type>) {
118 using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
119
120 info = &type_id<plain_type>();
121 vtable = basic_vtable<plain_type>;
122
123 if constexpr(std::is_lvalue_reference_v<Type>) {
124 static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
125 mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
126 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
127 instance = (std::addressof(args), ...);
128 } else if constexpr(in_situ<plain_type>) {
130
131 if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
132 ::new(&storage) plain_type{std::forward<Args>(args)...};
133 } else {
134 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
135 ::new(&storage) plain_type(std::forward<Args>(args)...);
136 }
137 } else {
138 mode = any_policy::dynamic;
139
140 if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
141 instance = new plain_type{std::forward<Args>(args)...};
142 } else if constexpr(std::is_array_v<plain_type>) {
143 static_assert(sizeof...(Args) == 0u, "Invalid arguments");
144 instance = new plain_type[std::extent_v<plain_type>]();
145 } else {
146 instance = new plain_type(std::forward<Args>(args)...);
147 }
148 }
149 }
150 }
151
152 basic_any(const basic_any &other, const any_policy pol) noexcept
153 : instance{other.data()},
154 info{other.info},
155 vtable{other.vtable},
156 mode{pol} {}
157
158public:
160 static constexpr auto length = Len;
162 static constexpr auto alignment = Align;
163
165 constexpr basic_any() noexcept
166 : basic_any{std::in_place_type<void>} {}
167
174 template<typename Type, typename... Args>
175 explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
176 : instance{} {
177 initialize<Type>(std::forward<Args>(args)...);
178 }
179
185 template<typename Type>
186 explicit basic_any(std::in_place_t, Type *value)
187 : instance{} {
188 static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
189
190 if(value != nullptr) {
191 initialize<Type &>(*value);
192 mode = any_policy::dynamic;
193 }
194 }
195
201 template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
202 basic_any(Type &&value)
203 : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
204
209 basic_any(const basic_any &other)
210 : basic_any{} {
211 if(other.vtable) {
212 other.vtable(request::copy, other, this);
213 }
214 }
215
220 basic_any(basic_any &&other) noexcept
221 : instance{},
222 info{other.info},
223 vtable{other.vtable},
224 mode{other.mode} {
225 if(other.mode == any_policy::embedded) {
226 other.vtable(request::move, other, this);
227 } else if(other.mode != any_policy::empty) {
228 instance = std::exchange(other.instance, nullptr);
229 }
230 }
231
234 if(owner()) {
235 vtable(request::destroy, *this, nullptr);
236 }
237 }
238
244 basic_any &operator=(const basic_any &other) {
245 if(this != &other) {
246 reset();
247
248 if(other.vtable) {
249 other.vtable(request::copy, other, this);
250 }
251 }
252
253 return *this;
254 }
255
265 basic_any &operator=(basic_any &&other) noexcept {
266 reset();
267
268 if(other.mode == any_policy::embedded) {
269 other.vtable(request::move, other, this);
270 } else if(other.mode != any_policy::empty) {
271 instance = std::exchange(other.instance, nullptr);
272 }
273
274 info = other.info;
275 vtable = other.vtable;
276 mode = other.mode;
277
278 return *this;
279 }
280
287 template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
288 basic_any &operator=(Type &&value) {
289 emplace<std::decay_t<Type>>(std::forward<Type>(value));
290 return *this;
291 }
292
297 [[nodiscard]] const type_info &type() const noexcept {
298 return (info == nullptr) ? type_id<void>() : *info;
299 }
300
305 [[nodiscard]] const void *data() const noexcept {
306 return (mode == any_policy::embedded) ? vtable(request::get, *this, nullptr) : instance;
307 }
308
314 [[nodiscard]] const void *data(const type_info &req) const noexcept {
315 return (type() == req) ? data() : nullptr;
316 }
317
322 [[nodiscard]] void *data() noexcept {
323 return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
324 }
325
331 [[nodiscard]] void *data(const type_info &req) noexcept {
332 return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
333 }
334
341 template<typename Type, typename... Args>
342 void emplace(Args &&...args) {
343 reset();
344 initialize<Type>(std::forward<Args>(args)...);
345 }
346
352 bool assign(const basic_any &other) {
353 if(vtable && mode != any_policy::cref && *info == other.type()) {
354 return (vtable(request::assign, *this, other.data()) != nullptr);
355 }
356
357 return false;
358 }
359
361 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
362 bool assign(basic_any &&other) {
363 if(vtable && mode != any_policy::cref && *info == other.type()) {
364 if(auto *val = other.data(); val) {
365 return (vtable(request::transfer, *this, val) != nullptr);
366 }
367
368 return (vtable(request::assign, *this, std::as_const(other).data()) != nullptr);
369 }
370
371 return false;
372 }
373
375 void reset() {
376 if(owner()) {
377 vtable(request::destroy, *this, nullptr);
378 }
379
380 instance = nullptr;
381 info = nullptr;
382 vtable = nullptr;
383 mode = any_policy::empty;
384 }
385
390 [[nodiscard]] explicit operator bool() const noexcept {
391 return vtable != nullptr;
392 }
393
399 [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
400 if(vtable && *info == other.type()) {
401 return (vtable(request::compare, *this, other.data()) != nullptr);
402 }
403
404 return (!vtable && !other.vtable);
405 }
406
412 [[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
413 return !(*this == other);
414 }
415
420 [[nodiscard]] basic_any as_ref() noexcept {
421 return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
422 }
423
425 [[nodiscard]] basic_any as_ref() const noexcept {
426 return basic_any{*this, any_policy::cref};
427 }
428
433 [[nodiscard]] bool owner() const noexcept {
434 return (mode == any_policy::dynamic || mode == any_policy::embedded);
435 }
436
441 [[nodiscard]] any_policy policy() const noexcept {
442 return mode;
443 }
444
445private:
446 union {
447 const void *instance;
449 };
450 const type_info *info{};
451 vtable_type *vtable{};
453};
454
463template<typename Type, std::size_t Len, std::size_t Align>
464[[nodiscard]] std::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
465 const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
466 ENTT_ASSERT(instance, "Invalid instance");
467 return static_cast<Type>(*instance);
468}
469
471template<typename Type, std::size_t Len, std::size_t Align>
472[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
473 // forces const on non-reference types to make them work also with wrappers for const references
474 auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
475 ENTT_ASSERT(instance, "Invalid instance");
476 return static_cast<Type>(*instance);
477}
478
480template<typename Type, std::size_t Len, std::size_t Align>
481// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
482[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
483 if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
484 if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
485 return static_cast<Type>(std::move(*instance));
486 }
487
488 return any_cast<Type>(data);
489 } else {
490 auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
491 ENTT_ASSERT(instance, "Invalid instance");
492 return static_cast<Type>(std::move(*instance));
493 }
494}
495
497template<typename Type, std::size_t Len, std::size_t Align>
498[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
499 const auto &info = type_id<std::remove_cv_t<Type>>();
500 return static_cast<const Type *>(data->data(info));
501}
502
504template<typename Type, std::size_t Len, std::size_t Align>
505[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
506 if constexpr(std::is_const_v<Type>) {
507 // last attempt to make wrappers for const references return their values
508 return any_cast<Type>(&std::as_const(*data));
509 } else {
510 const auto &info = type_id<std::remove_cv_t<Type>>();
511 return static_cast<Type *>(data->data(info));
512 }
513}
514
524template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
525[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
526 return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
527}
528
537template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
538[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
539 return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
540}
541
542} // namespace entt
543
544#endif
A SBO friendly, type-safe container for single values of any type.
Definition any.hpp:38
void reset()
Destroys contained object.
Definition any.hpp:375
constexpr basic_any() noexcept
Default constructor.
Definition any.hpp:165
basic_any(const basic_any &other)
Copy constructor.
Definition any.hpp:209
basic_any(std::in_place_type_t< Type >, Args &&...args)
Constructs a wrapper by directly initializing the new object.
Definition any.hpp:175
const void * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:305
bool owner() const noexcept
Returns true if a wrapper owns its object, false otherwise.
Definition any.hpp:433
bool assign(const basic_any &other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:352
bool assign(basic_any &&other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:362
static constexpr auto length
Definition any.hpp:160
void emplace(Args &&...args)
Definition any.hpp:342
const type_info & type() const noexcept
Returns the object type if any, type_id<void>() otherwise.
Definition any.hpp:297
bool operator!=(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:412
static constexpr auto alignment
Definition any.hpp:162
basic_any(basic_any &&other) noexcept
Move constructor.
Definition any.hpp:220
any_policy policy() const noexcept
Returns the current mode of an any object.
Definition any.hpp:441
basic_any(std::in_place_t, Type *value)
Constructs a wrapper taking ownership of the passed object.
Definition any.hpp:186
basic_any as_ref() noexcept
Aliasing constructor.
Definition any.hpp:420
~basic_any()
Frees the internal storage, whatever it means.
Definition any.hpp:233
basic_any & operator=(const basic_any &other)
Copy assignment operator.
Definition any.hpp:244
void * data(const type_info &req) noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:331
basic_any as_ref() const noexcept
Aliasing constructor.
Definition any.hpp:425
basic_any & operator=(Type &&value)
Value assignment operator.
Definition any.hpp:288
basic_any & operator=(basic_any &&other) noexcept
Move assignment operator.
Definition any.hpp:265
bool operator==(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:399
void * data() noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:322
basic_any(Type &&value)
Constructs a wrapper from a given value.
Definition any.hpp:202
const void * data(const type_info &req) const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:314
EnTT default namespace.
Definition dense_map.hpp:22
constexpr bool is_equality_comparable_v
Helper variable template.
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:464
constexpr get_t< Type... > get
Variable template for lists of observed elements.
Definition fwd.hpp:167
basic_any< Len, Align > make_any(Args &&...args)
Constructs a wrapper from a given type, passing it all arguments.
Definition any.hpp:525
const type_info & type_id() noexcept
Returns the type info object associated to a given type.
any_policy
Possible modes of an any object.
Definition fwd.hpp:11
@ ref
Aliasing mode, the object points to a non-const element.
Definition fwd.hpp:19
@ cref
Const aliasing mode, the object points to a const element.
Definition fwd.hpp:21
@ embedded
Owning mode, the object owns an embedded element.
Definition fwd.hpp:17
@ empty
Default mode, the object does not own any elements.
Definition fwd.hpp:13
@ dynamic
Owning mode, the object owns a 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:538
basic_storage< Type > storage
Alias declaration for the most common use case.
Definition fwd.hpp:78
Provides a common way to define storage types.
Definition fwd.hpp:226
Implementation specific information about a type.