EnTT 3.14.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 // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
54 alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
55 };
56
57 template<typename Type>
58 static constexpr bool in_situ = (Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
59
60 template<typename Type>
61 static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
62 static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
63 const Type *elem = nullptr;
64
65 if constexpr(in_situ<Type>) {
66 elem = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
67 } else {
68 elem = static_cast<const Type *>(value.instance);
69 }
70
71 switch(op) {
72 case operation::copy:
73 if constexpr(std::is_copy_constructible_v<Type>) {
74 static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
75 }
76 break;
77 case operation::move:
78 if constexpr(in_situ<Type>) {
79 if(value.mode == any_policy::owner) {
80 return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))};
81 }
82 }
83
84 return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
85 case operation::transfer:
86 if constexpr(std::is_move_assignable_v<Type>) {
87 *const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
88 return other;
89 }
90 [[fallthrough]];
91 case operation::assign:
92 if constexpr(std::is_copy_assignable_v<Type>) {
93 *const_cast<Type *>(elem) = *static_cast<const Type *>(other);
94 return other;
95 }
96 break;
97 case operation::destroy:
98 if constexpr(in_situ<Type>) {
99 elem->~Type();
100 } else if constexpr(std::is_array_v<Type>) {
101 delete[] elem;
102 } else {
103 delete elem;
104 }
105 break;
106 case operation::compare:
107 if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
108 return *elem == *static_cast<const Type *>(other) ? other : nullptr;
109 } else {
110 return (elem == other) ? other : nullptr;
111 }
112 case operation::get:
113 return elem;
114 }
115
116 return nullptr;
117 }
118
119 template<typename Type, typename... Args>
120 void initialize([[maybe_unused]] Args &&...args) {
121 using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
122 info = &type_id<plain_type>();
123
124 if constexpr(!std::is_void_v<Type>) {
126
127 if constexpr(std::is_lvalue_reference_v<Type>) {
128 static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
129 mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
130 instance = (std::addressof(args), ...);
131 } else if constexpr(in_situ<plain_type>) {
132 if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
133 ::new(&storage) plain_type{std::forward<Args>(args)...};
134 } else {
135 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
136 ::new(&storage) plain_type(std::forward<Args>(args)...);
137 }
138 } else {
139 if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
140 instance = new plain_type{std::forward<Args>(args)...};
141 } else if constexpr(std::is_array_v<plain_type>) {
142 static_assert(sizeof...(Args) == 0u, "Invalid arguments");
143 instance = new plain_type[std::extent_v<plain_type>]();
144 } else {
145 instance = new plain_type(std::forward<Args>(args)...);
146 }
147 }
148 }
149 }
150
152 : instance{other.data()},
153 info{other.info},
154 vtable{other.vtable},
155 mode{pol} {}
156
157public:
159 static constexpr auto length = Len;
161 static constexpr auto alignment = Align;
162
164 constexpr basic_any() noexcept
165 : basic_any{std::in_place_type<void>} {}
166
173 template<typename Type, typename... Args>
174 explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
175 : instance{},
176 info{},
177 vtable{},
178 mode{any_policy::owner} {
179 initialize<Type>(std::forward<Args>(args)...);
180 }
181
187 template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
188 basic_any(Type &&value)
189 : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
190
196 : basic_any{} {
197 if(other.vtable) {
198 other.vtable(operation::copy, other, this);
199 }
200 }
201
207 : instance{},
208 info{other.info},
209 vtable{other.vtable},
210 mode{other.mode} {
211 if(other.vtable) {
212 other.vtable(operation::move, other, this);
213 }
214 }
215
218 if(vtable && (mode == any_policy::owner)) {
219 vtable(operation::destroy, *this, nullptr);
220 }
221 }
222
229 if(this != &other) {
230 reset();
231
232 if(other.vtable) {
233 other.vtable(operation::copy, other, this);
234 }
235 }
236
237 return *this;
238 }
239
246 ENTT_ASSERT(this != &other, "Self move assignment");
247
248 reset();
249
250 if(other.vtable) {
251 other.vtable(operation::move, other, this);
252 info = other.info;
253 vtable = other.vtable;
254 mode = other.mode;
255 }
256
257 return *this;
258 }
259
266 template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
267 basic_any &operator=(Type &&value) {
268 emplace<std::decay_t<Type>>(std::forward<Type>(value));
269 return *this;
270 }
271
277 return *info;
278 }
279
284 [[nodiscard]] const void *data() const noexcept {
285 return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
286 }
287
293 [[nodiscard]] const void *data(const type_info &req) const noexcept {
294 return *info == req ? data() : nullptr;
295 }
296
301 [[nodiscard]] void *data() noexcept {
302 return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
303 }
304
310 [[nodiscard]] void *data(const type_info &req) noexcept {
311 return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
312 }
313
320 template<typename Type, typename... Args>
321 void emplace(Args &&...args) {
322 reset();
323 initialize<Type>(std::forward<Args>(args)...);
324 }
325
331 bool assign(const basic_any &other) {
332 if(vtable && mode != any_policy::cref && *info == *other.info) {
333 return (vtable(operation::assign, *this, other.data()) != nullptr);
334 }
335
336 return false;
337 }
338
341 if(vtable && mode != any_policy::cref && *info == *other.info) {
342 if(auto *val = other.data(); val) {
343 return (vtable(operation::transfer, *this, val) != nullptr);
344 }
345
346 return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
347 }
348
349 return false;
350 }
351
353 void reset() {
354 if(vtable && (mode == any_policy::owner)) {
355 vtable(operation::destroy, *this, nullptr);
356 }
357
358 // unnecessary but it helps to detect nasty bugs
359 ENTT_ASSERT((instance = nullptr) == nullptr, "");
360 info = &type_id<void>();
361 vtable = nullptr;
362 mode = any_policy::owner;
363 }
364
369 [[nodiscard]] explicit operator bool() const noexcept {
370 return vtable != nullptr;
371 }
372
378 [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
379 if(vtable && *info == *other.info) {
380 return (vtable(operation::compare, *this, other.data()) != nullptr);
381 }
382
383 return (!vtable && !other.vtable);
384 }
385
391 [[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
392 return !(*this == other);
393 }
394
402
407
413 return mode;
414 }
415
416private:
417 union {
418 const void *instance;
420 };
421 const type_info *info;
422 vtable_type *vtable;
423 any_policy mode;
424};
425
434template<typename Type, std::size_t Len, std::size_t Align>
435[[nodiscard]] std::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
436 const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
437 ENTT_ASSERT(instance, "Invalid instance");
438 return static_cast<Type>(*instance);
439}
440
442template<typename Type, std::size_t Len, std::size_t Align>
443[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
444 // forces const on non-reference types to make them work also with wrappers for const references
445 auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
446 ENTT_ASSERT(instance, "Invalid instance");
447 return static_cast<Type>(*instance);
448}
449
451template<typename Type, std::size_t Len, std::size_t Align>
452[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
453 if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
454 if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
455 return static_cast<Type>(std::move(*instance));
456 }
457
458 return any_cast<Type>(data);
459 } else {
460 auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
461 ENTT_ASSERT(instance, "Invalid instance");
462 return static_cast<Type>(std::move(*instance));
463 }
464}
465
467template<typename Type, std::size_t Len, std::size_t Align>
468[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
469 const auto &info = type_id<std::remove_cv_t<Type>>();
470 return static_cast<const Type *>(data->data(info));
471}
472
474template<typename Type, std::size_t Len, std::size_t Align>
475[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
476 if constexpr(std::is_const_v<Type>) {
477 // last attempt to make wrappers for const references return their values
478 return any_cast<Type>(&std::as_const(*data));
479 } else {
480 const auto &info = type_id<std::remove_cv_t<Type>>();
481 return static_cast<Type *>(data->data(info));
482 }
483}
484
496 return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
497}
498
509 return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
510}
511
512} // namespace entt
513
514#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:353
constexpr basic_any() noexcept
Default constructor.
Definition any.hpp:164
basic_any(const basic_any &other)
Copy constructor.
Definition any.hpp:195
basic_any(std::in_place_type_t< Type >, Args &&...args)
Constructs a wrapper by directly initializing the new object.
Definition any.hpp:174
const void * data() const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:284
bool assign(const basic_any &other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:331
bool assign(basic_any &&other)
Assigns a value to the contained object without replacing it.
Definition any.hpp:340
static constexpr auto length
Size of the internal storage.
Definition any.hpp:159
void emplace(Args &&...args)
Replaces the contained object by creating a new instance directly.
Definition any.hpp:321
const type_info & type() const noexcept
Returns the object type if any, type_id<void>() otherwise.
Definition any.hpp:276
bool operator!=(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:391
static constexpr auto alignment
Alignment requirement.
Definition any.hpp:161
basic_any(basic_any &&other) noexcept
Move constructor.
Definition any.hpp:206
any_policy policy() const noexcept
Returns the current mode of an any object.
Definition any.hpp:412
basic_any as_ref() noexcept
Aliasing constructor.
Definition any.hpp:399
~basic_any()
Frees the internal storage, whatever it means.
Definition any.hpp:217
basic_any & operator=(const basic_any &other)
Copy assignment operator.
Definition any.hpp:228
void * data(const type_info &req) noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:310
basic_any as_ref() const noexcept
Aliasing constructor.
Definition any.hpp:404
basic_any & operator=(Type &&value)
Value assignment operator.
Definition any.hpp:267
basic_any & operator=(basic_any &&other) noexcept
Move assignment operator.
Definition any.hpp:245
bool operator==(const basic_any &other) const noexcept
Checks if two wrappers differ in their content.
Definition any.hpp:378
void * data() noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:301
basic_any(Type &&value)
Constructs a wrapper from a given value.
Definition any.hpp:188
const void * data(const type_info &req) const noexcept
Returns an opaque pointer to the contained instance.
Definition any.hpp:293
Storage implementation.
Definition storage.hpp:230
EnTT default namespace.
Definition dense_map.hpp:22
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
Definition memory.hpp:219
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:435
constexpr get_t< Type... > get
Variable template for lists of observed elements.
Definition fwd.hpp:168
basic_any< Len, Align > make_any(Args &&...args)
Constructs a wrapper from a given type, passing it all arguments.
Definition any.hpp:495
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:508
Provides a common way to define storage types.
Definition fwd.hpp:227
Implementation specific information about a type.