EnTT 3.11.0
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
21namespace internal {
22
23enum class any_operation : std::uint8_t {
24 copy,
25 move,
26 transfer,
27 assign,
28 destroy,
29 compare,
30 get
31};
32
33enum class any_policy : std::uint8_t {
34 owner,
35 ref,
36 cref
37};
38
39} // namespace internal
40
51template<std::size_t Len, std::size_t Align>
52class basic_any {
53 using operation = internal::any_operation;
54 using policy = internal::any_policy;
55 using vtable_type = const void *(const operation, const basic_any &, const void *);
56
57 struct storage_type {
58 alignas(Align) std::byte data[Len + !Len];
59 };
60
61 template<typename Type>
62 static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v<Type>;
63
64 template<typename Type>
65 static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
66 static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
67 const Type *element = nullptr;
68
69 if constexpr(in_situ<Type>) {
70 element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
71 } else {
72 element = static_cast<const Type *>(value.instance);
73 }
74
75 switch(op) {
76 case operation::copy:
77 if constexpr(std::is_copy_constructible_v<Type>) {
78 static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
79 }
80 break;
81 case operation::move:
82 if constexpr(in_situ<Type>) {
83 if(value.owner()) {
84 return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
85 }
86 }
87
88 return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
89 case operation::transfer:
90 if constexpr(std::is_move_assignable_v<Type>) {
91 *const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
92 return other;
93 }
94 [[fallthrough]];
95 case operation::assign:
96 if constexpr(std::is_copy_assignable_v<Type>) {
97 *const_cast<Type *>(element) = *static_cast<const Type *>(other);
98 return other;
99 }
100 break;
101 case operation::destroy:
102 if constexpr(in_situ<Type>) {
103 element->~Type();
104 } else if constexpr(std::is_array_v<Type>) {
105 delete[] element;
106 } else {
107 delete element;
108 }
109 break;
110 case operation::compare:
111 if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
112 return *element == *static_cast<const Type *>(other) ? other : nullptr;
113 } else {
114 return (element == other) ? other : nullptr;
115 }
116 case operation::get:
117 return element;
118 }
119
120 return nullptr;
121 }
122
123 template<typename Type, typename... Args>
124 void initialize([[maybe_unused]] Args &&...args) {
125 info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
126
127 if constexpr(!std::is_void_v<Type>) {
128 vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
129
130 if constexpr(std::is_lvalue_reference_v<Type>) {
131 static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
132 mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
133 instance = (std::addressof(args), ...);
134 } else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
135 if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
136 new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
137 } else {
138 new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
139 }
140 } else {
141 if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
142 instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
143 } else {
144 instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
145 }
146 }
147 }
148 }
149
150 basic_any(const basic_any &other, const policy pol) noexcept
151 : instance{other.data()},
152 info{other.info},
153 vtable{other.vtable},
154 mode{pol} {}
155
156public:
158 static constexpr auto length = Len;
160 static constexpr auto alignment = Align;
161
163 constexpr basic_any() noexcept
164 : basic_any{std::in_place_type<void>} {}
165
172 template<typename Type, typename... Args>
173 explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
174 : instance{},
175 info{},
176 vtable{},
177 mode{policy::owner} {
178 initialize<Type>(std::forward<Args>(args)...);
179 }
180
186 template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
187 basic_any(Type &&value)
188 : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
189
194 basic_any(const basic_any &other)
195 : basic_any{} {
196 if(other.vtable) {
197 other.vtable(operation::copy, other, this);
198 }
199 }
200
205 basic_any(basic_any &&other) noexcept
206 : instance{},
207 info{other.info},
208 vtable{other.vtable},
209 mode{other.mode} {
210 if(other.vtable) {
211 other.vtable(operation::move, other, this);
212 }
213 }
214
217 if(vtable && owner()) {
218 vtable(operation::destroy, *this, nullptr);
219 }
220 }
221
228 reset();
229
230 if(other.vtable) {
231 other.vtable(operation::copy, other, this);
232 }
233
234 return *this;
235 }
236
242 basic_any &operator=(basic_any &&other) noexcept {
243 reset();
244
245 if(other.vtable) {
246 other.vtable(operation::move, other, this);
247 info = other.info;
248 vtable = other.vtable;
249 mode = other.mode;
250 }
251
252 return *this;
253 }
254
261 template<typename Type>
262 std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
263 operator=(Type &&value) {
264 emplace<std::decay_t<Type>>(std::forward<Type>(value));
265 return *this;
266 }
267
272 [[nodiscard]] const type_info &type() const noexcept {
273 return *info;
274 }
275
280 [[nodiscard]] const void *data() const noexcept {
281 return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
282 }
283
289 [[nodiscard]] const void *data(const type_info &req) const noexcept {
290 return *info == req ? data() : nullptr;
291 }
292
297 [[nodiscard]] void *data() noexcept {
298 return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
299 }
300
306 [[nodiscard]] void *data(const type_info &req) noexcept {
307 return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
308 }
309
316 template<typename Type, typename... Args>
317 void emplace(Args &&...args) {
318 reset();
319 initialize<Type>(std::forward<Args>(args)...);
320 }
321
327 bool assign(const basic_any &other) {
328 if(vtable && mode != policy::cref && *info == *other.info) {
329 return (vtable(operation::assign, *this, other.data()) != nullptr);
330 }
331
332 return false;
333 }
334
336 bool assign(basic_any &&other) {
337 if(vtable && mode != policy::cref && *info == *other.info) {
338 if(auto *val = other.data(); val) {
339 return (vtable(operation::transfer, *this, val) != nullptr);
340 } else {
341 return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
342 }
343 }
344
345 return false;
346 }
347
349 void reset() {
350 if(vtable && owner()) {
351 vtable(operation::destroy, *this, nullptr);
352 }
353
354 // unnecessary but it helps to detect nasty bugs
355 ENTT_ASSERT((instance = nullptr) == nullptr, "");
356 info = &type_id<void>();
357 vtable = nullptr;
358 mode = policy::owner;
359 }
360
365 [[nodiscard]] explicit operator bool() const noexcept {
366 return vtable != nullptr;
367 }
368
374 [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
375 if(vtable && *info == *other.info) {
376 return (vtable(operation::compare, *this, other.data()) != nullptr);
377 }
378
379 return (!vtable && !other.vtable);
380 }
381
387 [[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
388 return !(*this == other);
389 }
390
395 [[nodiscard]] basic_any as_ref() noexcept {
396 return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
397 }
398
400 [[nodiscard]] basic_any as_ref() const noexcept {
401 return basic_any{*this, policy::cref};
402 }
403
408 [[nodiscard]] bool owner() const noexcept {
409 return (mode == policy::owner);
410 }
411
412private:
413 union {
414 const void *instance;
416 };
417 const type_info *info;
418 vtable_type *vtable;
419 policy mode;
420};
421
430template<typename Type, std::size_t Len, std::size_t Align>
431Type any_cast(const basic_any<Len, Align> &data) noexcept {
432 const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
433 ENTT_ASSERT(instance, "Invalid instance");
434 return static_cast<Type>(*instance);
435}
436
438template<typename Type, std::size_t Len, std::size_t Align>
439Type any_cast(basic_any<Len, Align> &data) noexcept {
440 // forces const on non-reference types to make them work also with wrappers for const references
441 auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
442 ENTT_ASSERT(instance, "Invalid instance");
443 return static_cast<Type>(*instance);
444}
445
447template<typename Type, std::size_t Len, std::size_t Align>
448Type any_cast(basic_any<Len, Align> &&data) noexcept {
449 if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
450 if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
451 return static_cast<Type>(std::move(*instance));
452 } else {
453 return any_cast<Type>(data);
454 }
455 } else {
456 auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
457 ENTT_ASSERT(instance, "Invalid instance");
458 return static_cast<Type>(std::move(*instance));
459 }
460}
461
463template<typename Type, std::size_t Len, std::size_t Align>
464const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
465 const auto &info = type_id<std::remove_cv_t<Type>>();
466 return static_cast<const Type *>(data->data(info));
467}
468
470template<typename Type, std::size_t Len, std::size_t Align>
471Type *any_cast(basic_any<Len, Align> *data) noexcept {
472 if constexpr(std::is_const_v<Type>) {
473 // last attempt to make wrappers for const references return their values
474 return any_cast<Type>(&std::as_const(*data));
475 } else {
476 const auto &info = type_id<std::remove_cv_t<Type>>();
477 return static_cast<Type *>(data->data(info));
478 }
479}
480
490template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
492 return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
493}
494
503template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
505 return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
506}
507
508} // namespace entt
509
510#endif
A SBO friendly, type-safe container for single values of any type.
Definition: any.hpp:52
void reset()
Destroys contained object.
Definition: any.hpp:349
constexpr basic_any() noexcept
Default constructor.
Definition: any.hpp:163
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:263
basic_any(const basic_any &other)
Copy constructor.
Definition: any.hpp:194
basic_any(std::in_place_type_t< Type >, Args &&...args)
Constructs a wrapper by directly initializing the new object.
Definition: any.hpp:173
const void * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition: any.hpp:280
bool owner() const noexcept
Returns true if a wrapper owns its object, false otherwise.
Definition: any.hpp:408
bool assign(const basic_any &other)
Assigns a value to the contained object without replacing it.
Definition: any.hpp:327
bool assign(basic_any &&other)
Assigns a value to the contained object without replacing it.
Definition: any.hpp:336
static constexpr auto length
Size of the internal storage.
Definition: any.hpp:158
void emplace(Args &&...args)
Replaces the contained object by creating a new instance directly.
Definition: any.hpp:317
const type_info & type() const noexcept
Returns the object type if any, type_id<void>() otherwise.
Definition: any.hpp:272
bool operator!=(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition: any.hpp:387
static constexpr auto alignment
Alignment requirement.
Definition: any.hpp:160
basic_any(basic_any &&other) noexcept
Move constructor.
Definition: any.hpp:205
basic_any as_ref() noexcept
Aliasing constructor.
Definition: any.hpp:395
~basic_any()
Frees the internal storage, whatever it means.
Definition: any.hpp:216
basic_any & operator=(const basic_any &other)
Copy assignment operator.
Definition: any.hpp:227
void * data(const type_info &req) noexcept
Returns an opaque pointer to the contained instance.
Definition: any.hpp:306
basic_any as_ref() const noexcept
Aliasing constructor.
Definition: any.hpp:400
basic_any & operator=(basic_any &&other) noexcept
Move assignment operator.
Definition: any.hpp:242
bool operator==(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition: any.hpp:374
void * data() noexcept
Returns an opaque pointer to the contained instance.
Definition: any.hpp:297
basic_any(Type &&value)
Constructs a wrapper from a given value.
Definition: any.hpp:187
const void * data(const type_info &req) const noexcept
Returns an opaque pointer to the contained instance.
Definition: any.hpp:289
Basic storage implementation.
Definition: storage.hpp:234
EnTT default namespace.
Definition: dense_map.hpp:21
constexpr get_t< Type... > get
Variable template for lists of observed components.
Definition: fwd.hpp:85
Type any_cast(const basic_any< Len, Align > &data) noexcept
Performs type-safe access to the contained object.
Definition: any.hpp:431
basic_any< Len, Align > make_any(Args &&...args)
Constructs a wrapper from a given type, passing it all arguments.
Definition: any.hpp:491
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition: any.hpp:504
Provides a common way to define storage types.
Definition: storage.hpp:908
Implementation specific information about a type.
Definition: type_info.hpp:141