Project Alice
Loading...
Searching...
No Matches
cyto_any.hpp
Go to the documentation of this file.
1#pragma once
2
3//
4// This file is derived from https://github.com/kocienda/Any
5// With *very* minor tweaks
6//
7
8//
9// cyto-any.h
10//
11// A class modeled on std::any.
12//
13// MIT License
14//
15// Copyright (c) 2020 Ken Kocienda
16//
17// Permission is hereby granted, free of charge, to any person obtaining a copy
18// of this software and associated documentation files (the "Software"), to deal
19// in the Software without restriction, including without limitation the rights
20// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21// copies of the Software, and to permit persons to whom the Software is
22// furnished to do so, subject to the following conditions:
23//
24// The above copyright notice and this permission notice shall be included in all
25// copies or substantial portions of the Software.
26//
27// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33// SOFTWARE.
34
35#include <exception>
36#include <initializer_list>
37#include <new>
38#include <type_traits>
39#include <typeinfo>
40#include <utility>
41
42#include <string.h>
43
44#ifndef _MSC_VER
45#ifndef ANY_ALWAYS_INLINE
46#ifdef NDEBUG
47#define ANY_ALWAYS_INLINE inline __attribute__((__visibility__("hidden"), __always_inline__))
48#else
49#define ANY_ALWAYS_INLINE inline
50#endif
51#endif
52#else
53#ifndef ANY_ALWAYS_INLINE
54#ifdef NDEBUG
55#define ANY_ALWAYS_INLINE inline __forceinline
56#else
57#define ANY_ALWAYS_INLINE inline
58#endif
59#endif
60#endif
61
62// #ifdef __cpp_rtti
63// #define ANY_USE_TYPEINFO 1
64// #else
65#define ANY_USE_TYPEINFO 0
66// #endif
67
68// #ifdef __cpp_exceptions
69// #define ANY_USE_EXCEPTIONS 1
70// #else
71#define ANY_USE_EXCEPTIONS 0
72// #endif
73
74#ifndef ANY_USE_SMALL_MEMCPY_STRATEGY
75#define ANY_USE_SMALL_MEMCPY_STRATEGY 1
76#endif
77
78#define ANY_USE(FEATURE) (defined ANY_USE_##FEATURE && ANY_USE_##FEATURE)
79
80namespace Cyto {
81
82#if ANY_USE_EXCEPTIONS
83class bad_any_cast : public std::bad_cast { };
84#endif // ANY_USE(EXCEPTIONS)
85
86static ANY_ALWAYS_INLINE void handle_bad_any_cast() {
87#if ANY_USE_EXCEPTIONS
88 throw bad_any_cast();
89#else
90 abort();
91#endif
92}
93
94template<class T>
95struct IsInPlaceType_ : std::false_type { };
96template<>
97struct IsInPlaceType_<std::in_place_t> : std::true_type { };
98template<class T>
99struct IsInPlaceType_<std::in_place_type_t<T>> : std::true_type { };
100template<size_t S>
101struct IsInPlaceType_<std::in_place_index_t<S>> : std::true_type { };
102template<class T>
104
105constexpr size_t StorageBufferSize = 8 * sizeof(void*);
106using StorageBuffer = std::aligned_storage_t<StorageBufferSize, std::alignment_of_v<void*>>;
107
108template<class T>
110 std::bool_constant<sizeof(T) <= sizeof(StorageBuffer) && std::alignment_of_v<StorageBuffer> % std::alignment_of_v<T> == 0>;
111
112template<class T>
114
115union Storage {
116 constexpr Storage() { }
117 Storage(Storage const&) = delete;
118 Storage(Storage&&) = delete;
119 Storage& operator=(Storage const&) = delete;
121
123};
124
125#if !ANY_USE_TYPEINFO
126template<class T>
128 static int id;
129};
130
131template<class T>
133
134template<class T>
135ANY_ALWAYS_INLINE constexpr void const* fallback_typeid() {
137}
138#endif // !ANY_USE(TYPEINFO)
139
141static constexpr void* void_get(Storage* s, void const* info) {
142 return nullptr;
143}
144
146static constexpr void void_copy(Storage* dst, Storage const* src) { }
147
149static constexpr void void_move(Storage* dst, Storage* src) { }
150
152static constexpr void void_drop(Storage* s) { }
153
155 using Get = void* (*)(Storage* s, void const* type);
156 using Copy = void (*)(Storage* dst, Storage const* src);
157 using Move = void (*)(Storage* dst, Storage* src);
158 using Drop = void (*)(Storage* s);
159
160 constexpr AnyActions() noexcept { }
161
162 constexpr AnyActions(Get g, Copy c, Move m, Drop d, void const* t) noexcept : get(g), copy(c), move(m), drop(d), type(t) { }
163
164 Get get = void_get;
165 Copy copy = void_copy;
166 Move move = void_move;
167 Drop drop = void_drop;
168#if ANY_USE_TYPEINFO
169 void const* type = static_cast<void const*>(&typeid(void));
170#else
171 void const* type = fallback_typeid<void>();
172#endif
173};
174
175template<class T>
176struct AnyTraits {
177#if ANY_USE_SMALL_MEMCPY_STRATEGY
178 template<class X = T, class... Args, std::enable_if_t<IsStorageBufferSized<X> && std::is_trivially_copyable_v<X>, int> = 0>
179 ANY_ALWAYS_INLINE static X& make(Storage* s, std::in_place_type_t<X> vtype, Args&&... args) {
180 X v(std::forward<Args>(args)...);
181 memcpy(&s->buf, static_cast<void*>(&v), sizeof(X));
182 return *(static_cast<X*>(static_cast<void*>(&s->buf)));
183 }
184
185 template< class X = T, class... Args,
186 std::enable_if_t<IsStorageBufferSized<X> && !std::is_trivially_copyable_v<X> && std::is_nothrow_move_constructible_v<X>,
187 int> = 0>
188 ANY_ALWAYS_INLINE static X& make(Storage* s, std::in_place_type_t<X> vtype, Args&&... args) {
189 return *(::new(static_cast<void*>(&s->buf)) X(std::forward<Args>(args)...));
190 }
191#else // ANY_USE(SMALL_MEMCPY_STRATEGY)
192 template<class X = T, class... Args,
193 std::enable_if_t<IsStorageBufferSized<X> && std::is_nothrow_move_constructible_v<X>, int> = 0>
194 ANY_ALWAYS_INLINE static X& make(Storage* s, std::in_place_type_t<X> vtype, Args&&... args) {
195 return *(::new(static_cast<void*>(&s->buf)) X(std::forward<Args>(args)...));
196 }
197#endif // ANY_USE(SMALL_MEMCPY_STRATEGY)
198
199private:
200 AnyTraits(AnyTraits const&) = default;
201 AnyTraits(AnyTraits&&) = default;
202 AnyTraits& operator=(AnyTraits const&) = default;
203 AnyTraits& operator=(AnyTraits&&) = default;
204
205 template<class X = T>
206 ANY_ALWAYS_INLINE static bool compare_typeid(void const* id) {
207#if ANY_USE_TYPEINFO
208 return *(static_cast< std::type_info const*>(id)) == typeid(X);
209#else
210 return (id && id == fallback_typeid<X>());
211#endif
212 }
213
214 //
215 // get
216 //
217 template<class X = T, std::enable_if_t<std::is_same_v<X, void>, int> = 0>
218 ANY_ALWAYS_INLINE static void* get(Storage* s, void const* type) {
219 return nullptr;
220 }
221
222 template<class X = T, std::enable_if_t<IsStorageBufferSized<X>, int> = 0>
223 ANY_ALWAYS_INLINE static void* get(Storage* s, void const* type) {
224 if(compare_typeid<X>(type)) {
225 return static_cast<void*>(&s->buf);
226 }
227 return nullptr;
228 }
229
230 //
231 // copy
232 //
233 template<class X = T, std::enable_if_t<std::is_same_v<X, void>, int> = 0>
234 ANY_ALWAYS_INLINE static void copy(Storage* dst, Storage const* src) { }
235
236#if ANY_USE_SMALL_MEMCPY_STRATEGY
237 template<class X = T, std::enable_if_t<IsStorageBufferSized<X> && std::is_trivially_copyable_v<X>, int> = 0>
238 ANY_ALWAYS_INLINE static void copy(Storage* dst, Storage const* src) {
239 memcpy(static_cast<void*>(&dst->buf), static_cast<void*>(const_cast<StorageBuffer*>(&src->buf)), sizeof(X));
240 }
241
242 template< class X = T,
243 std::enable_if_t<IsStorageBufferSized<X> && !std::is_trivially_copyable_v<X> && std::is_nothrow_move_constructible_v<X>,
244 int> = 0>
245 ANY_ALWAYS_INLINE static void copy(Storage* dst, Storage const* src) {
246 AnyTraits::make(dst, std::in_place_type_t<X>(), *static_cast<X const*>(static_cast<void const*>(&src->buf)));
247 }
248#else // ANY_USE(SMALL_MEMCPY_STRATEGY)
249 template<class X = T, std::enable_if_t<IsStorageBufferSized<X> && std::is_nothrow_move_constructible_v<X>, int> = 0>
250 ANY_ALWAYS_INLINE static void copy(Storage* dst, Storage const* src) {
251 AnyTraits::make(dst, std::in_place_type_t<X>(), *static_cast<X const*>(static_cast<void const*>(&src->buf)));
252 }
253#endif // ANY_USE(SMALL_MEMCPY_STRATEGY)
254
255 //
256 // move
257 //
258#if ANY_USE_SMALL_MEMCPY_STRATEGY
259 template<class X = T, std::enable_if_t<std::is_same_v<X, void>, int> = 0>
260 ANY_ALWAYS_INLINE static void move(Storage* dst, Storage* src) { }
261
262 template<class X = T, std::enable_if_t<IsStorageBufferSized<X>, int> = 0>
263 ANY_ALWAYS_INLINE static void move(Storage* dst, Storage* src) {
264 memcpy(static_cast<void*>(&dst->buf), static_cast<void*>(const_cast<StorageBuffer*>(&src->buf)), sizeof(X));
265 }
266#else // ANY_USE(SMALL_MEMCPY_STRATEGY)
267 template<class X = T, std::enable_if_t<IsStorageBufferSized<X>, int> = 0>
268 ANY_ALWAYS_INLINE static void move(Storage* dst, Storage* src) {
269 AnyTraits::make(dst, std::in_place_type_t<X>(), std::move(*static_cast<X const*>(static_cast<void const*>(&src->buf))));
270 }
271#endif // ANY_USE(SMALL_MEMCPY_STRATEGY)
272
273 //
274 // drop
275 //
276 template<class X = T, std::enable_if_t<std::is_same_v<X, void>, int> = 0>
277 ANY_ALWAYS_INLINE static void drop(Storage* s) { }
278
279 template<class X = T, std::enable_if_t<std::is_trivially_destructible_v<X>, int> = 0>
280 ANY_ALWAYS_INLINE static void drop(Storage* s) { }
281
282 template<class X = T, std::enable_if_t<IsStorageBufferSized<X> && !std::is_trivially_destructible_v<X>, int> = 0>
283 ANY_ALWAYS_INLINE static void drop(Storage* s) {
284 X& t = *static_cast<X*>(static_cast<void*>(const_cast<StorageBuffer*>(&s->buf)));
285 t.~X();
286 }
287
288public:
289 static constexpr AnyActions actions = AnyActions(get<T>, copy<T>, move<T>, drop<T>,
291 &typeid(T)
292#else
293 fallback_typeid<T>()
294#endif
295 );
296};
297
298class Any;
299
300template<class V, class T = std::decay_t<V>>
301using IsAnyConstructible_ = std::bool_constant<!std::is_same_v<T, Any> && !IsInPlaceType<V> && std::is_copy_constructible_v<T>>;
302
303template<class V>
305
306template<class T, class U, class... Args>
308 std::bool_constant<std::is_constructible_v<T, std::initializer_list<U>&, Args...> && std::is_copy_constructible_v<T>>;
309
310template<class T, class U, class... Args>
312
313class Any {
314public:
315 constexpr Any() noexcept : actions(VoidAnyActions) { }
316
317 template<class V, class T = std::decay_t<V>, std::enable_if_t<IsAnyConstructible<V>, int> = 0>
318 Any(V&& v) : actions(&AnyTraits<T>::actions) {
319 AnyTraits<T>::make(&storage, std::in_place_type_t<T>(), std::forward<V>(v));
320 }
321
322 template<class V, class... Args, class T = std::decay_t<V>, std::enable_if_t<IsAnyConstructible<T>, int> = 0>
323 explicit Any(std::in_place_type_t<V> vtype, Args&&... args) : actions(&AnyTraits<T>::actions) {
324 AnyTraits<T>::make(&storage, vtype, std::forward<Args>(args)...);
325 }
326
327 template<class V, class U, class... Args, class T = std::decay_t<V>,
328 std::enable_if_t<IsAnyInitializerListConstructible<T, U, Args...>, int> = 0>
329 explicit Any(std::in_place_type_t<V> vtype, std::initializer_list<U> list, Args&&... args) : actions(&AnyTraits<T>::actions) {
330 AnyTraits<T>::make(&storage, vtype, V{list, std::forward<Args>(args)...});
331 }
332
333 Any(Any const& other) : actions(other.actions) {
334 actions->copy(&storage, &other.storage);
335 }
336
337 Any(Any&& other) noexcept : actions(other.actions) {
338 actions->move(&storage, &other.storage);
339 other.actions = VoidAnyActions;
340 }
341
342 Any& operator=(Any const& other) {
343 if(this != &other) {
344 actions->drop(&storage);
345 actions = other.actions;
346 actions->copy(&storage, &other.storage);
347 }
348 return *this;
349 }
350
351 Any& operator=(Any&& other) noexcept {
352 if(this != &other) {
353 actions->drop(&storage);
354 actions = other.actions;
355 actions->move(&storage, &other.storage);
356 other.actions = VoidAnyActions;
357 }
358 return *this;
359 }
360
361 template<class V, class T = std::decay_t<V>, std::enable_if_t<IsAnyConstructible<T>, int> = 0>
362 Any& operator=(V&& v) {
363 *this = Any(std::forward<V>(v));
364 return *this;
365 }
366
368 actions->drop(&storage);
369 }
370
371 template<class V, class... Args, class T = std::decay_t<V>,
372 std::enable_if_t<std::is_constructible_v<T, Args...> && std::is_copy_constructible_v<T>, int> = 0>
373 T& emplace(Args&&... args) {
374 actions->drop(&storage);
375 actions = &AnyTraits<T>::actions;
376 return AnyTraits<T>::make(&storage, std::in_place_type_t<T>(), std::forward<Args>(args)...);
377 }
378
379 template<class V, class U, class... Args, class T = std::decay_t<V>,
380 std::enable_if_t<IsAnyInitializerListConstructible<T, U, Args...>, int> = 0>
381 T& emplace(std::initializer_list<U> list, Args&&... args) {
382 reset();
383 actions = &AnyTraits<T>::actions;
384 return AnyTraits<T>::make(&storage, std::in_place_type_t<T>(), V{list, std::forward<Args>(args)...});
385 }
386
388 void reset() {
389 actions->drop(&storage);
390 actions = VoidAnyActions;
391 }
392
394 void swap(Any& rhs) noexcept {
395 if(this == &rhs) {
396 return;
397 }
398
399 Any tmp;
400
401 // swap storage
402 rhs.actions->move(&tmp.storage, &rhs.storage);
403 actions->move(&rhs.storage, &storage);
404 rhs.actions->move(&storage, &tmp.storage);
405
406 // swap actions
407 tmp.actions = rhs.actions;
408 rhs.actions = actions;
409 actions = tmp.actions;
410 }
411
412 template<bool B>
413 ANY_ALWAYS_INLINE bool has_value() const noexcept {
414 return (actions != VoidAnyActions) == B;
415 }
416
418 bool has_value() const noexcept {
419 return has_value<true>();
420 }
421
422#if ANY_USE_TYPEINFO
423 std::type_info const& type() const noexcept {
424 return *static_cast< std::type_info const*>(actions->type);
425 }
426#endif
427
428 template<typename CHECK_TYPE>
429 ANY_ALWAYS_INLINE bool holds_type() const noexcept {
430#if ANY_USE_TYPEINFO
431 return typeid(CHECK_TYPE) == *static_cast< std::type_info const*>(actions->type);
432#else
433 return (actions->type == fallback_typeid<CHECK_TYPE>());
434#endif
435 }
436
437 template<class V>
438 friend std::remove_cv_t<std::remove_reference_t<V>>* any_cast(Any* a) noexcept;
439
440private:
441 static constexpr AnyActions _VoidAnyActions = AnyActions();
442 static constexpr AnyActions const* const VoidAnyActions = &_VoidAnyActions;
443
444 AnyActions const* actions;
445 Storage storage;
446};
447
449void swap(Any& lhs, Any& rhs) noexcept {
450 lhs.swap(rhs);
451}
452
453template<class T, class... Args>
455 return Any(std::in_place_type<T>, std::forward<Args>(args)...);
456}
457
458template<class T, class U, class... Args>
459ANY_ALWAYS_INLINE Any make_any(std::initializer_list<U> il, Args&&... args) {
460 return Any(std::in_place_type<T>, il, std::forward<Args>(args)...);
461}
462
463template<class V, class T = std::remove_cv_t<std::remove_reference_t<V>>,
464 std::enable_if_t<std::is_constructible<V, T const&>{}, int> = 0>
465V any_cast(Any const& a) {
466 auto tmp = any_cast<std::add_const_t<T>>(&a);
467 if(tmp == nullptr) {
468 handle_bad_any_cast();
469 }
470 return static_cast<V>(*tmp);
471}
472
473template<class V, class T = std::remove_cv_t<std::remove_reference_t<V>>,
474 std::enable_if_t<std::is_constructible<V, T const&>{}, int> = 0>
476 auto tmp = any_cast<T>(&a);
477 if(tmp == nullptr) {
478 handle_bad_any_cast();
479 }
480 return static_cast<V>(*tmp);
481}
482
483template<class V, class T = std::remove_cv_t<std::remove_reference_t<V>>,
484 std::enable_if_t<std::is_constructible<V, T const&>{}, int> = 0>
485V any_cast(Any&& a) {
486 auto tmp = any_cast<T>(&a);
487 if(tmp == nullptr) {
488 handle_bad_any_cast();
489 }
490 return static_cast<V>(std::move(*tmp));
491}
492
493template<class V, class T = std::remove_cv_t<std::remove_reference_t<V>>>
494T const* any_cast(Any const* a) noexcept {
495 return any_cast<V>(const_cast<Any*>(a));
496}
497
498template<class V>
499std::remove_cv_t<std::remove_reference_t<V>>* any_cast(Any* a) noexcept {
500 using T = std::remove_cv_t<std::remove_reference_t<V>>;
501 using U = std::decay_t<V>;
502 if(a && a->has_value()) {
503 void* p = a->actions->get(&a->storage,
505 &typeid(U)
506#else
507 fallback_typeid<U>()
508#endif
509 );
510 return (std::is_function<V>{}) ? nullptr : static_cast<T*>(p);
511 }
512 return nullptr;
513}
514
515} // namespace Cyto
Any(Any &&other) noexcept
Definition: cyto_any.hpp:337
Any(std::in_place_type_t< V > vtype, std::initializer_list< U > list, Args &&... args)
Definition: cyto_any.hpp:329
Any & operator=(Any &&other) noexcept
Definition: cyto_any.hpp:351
Any(Any const &other)
Definition: cyto_any.hpp:333
ANY_ALWAYS_INLINE bool holds_type() const noexcept
Definition: cyto_any.hpp:429
Any & operator=(V &&v)
Definition: cyto_any.hpp:362
ANY_ALWAYS_INLINE bool has_value() const noexcept
Definition: cyto_any.hpp:418
Any(V &&v)
Definition: cyto_any.hpp:318
T & emplace(Args &&... args)
Definition: cyto_any.hpp:373
constexpr Any() noexcept
Definition: cyto_any.hpp:315
ANY_ALWAYS_INLINE void reset()
Definition: cyto_any.hpp:388
ANY_ALWAYS_INLINE bool has_value() const noexcept
Definition: cyto_any.hpp:413
T & emplace(std::initializer_list< U > list, Args &&... args)
Definition: cyto_any.hpp:381
ANY_ALWAYS_INLINE void swap(Any &rhs) noexcept
Definition: cyto_any.hpp:394
Any(std::in_place_type_t< V > vtype, Args &&... args)
Definition: cyto_any.hpp:323
Any & operator=(Any const &other)
Definition: cyto_any.hpp:342
#define B(name, bit)
Definition: cpu.h:216
#define X(name, r, bit)
Definition: cpu.h:146
#define ANY_ALWAYS_INLINE
Definition: cyto_any.hpp:49
#define ANY_USE_TYPEINFO
Definition: cyto_any.hpp:65
std::bool_constant< std::is_constructible_v< T, std::initializer_list< U > &, Args... > &&std::is_copy_constructible_v< T > > IsAnyInitializerListConstructible_
Definition: cyto_any.hpp:308
std::bool_constant< sizeof(T)<=sizeof(StorageBuffer) &&std::alignment_of_v< StorageBuffer > % std::alignment_of_v< T >==0 > IsStorageBufferSized_
Definition: cyto_any.hpp:110
V any_cast(Any const &a)
Definition: cyto_any.hpp:465
ANY_ALWAYS_INLINE constexpr void const * fallback_typeid()
Definition: cyto_any.hpp:135
constexpr size_t StorageBufferSize
Definition: cyto_any.hpp:105
constexpr bool IsAnyConstructible
Definition: cyto_any.hpp:304
constexpr bool IsInPlaceType
Definition: cyto_any.hpp:103
ANY_ALWAYS_INLINE void swap(Any &lhs, Any &rhs) noexcept
Definition: cyto_any.hpp:449
std::aligned_storage_t< StorageBufferSize, std::alignment_of_v< void * > > StorageBuffer
Definition: cyto_any.hpp:106
std::bool_constant<!std::is_same_v< T, Any > &&!IsInPlaceType< V > &&std::is_copy_constructible_v< T > > IsAnyConstructible_
Definition: cyto_any.hpp:301
constexpr bool IsAnyInitializerListConstructible
Definition: cyto_any.hpp:311
constexpr bool IsStorageBufferSized
Definition: cyto_any.hpp:113
ANY_ALWAYS_INLINE Any make_any(Args &&... args)
Definition: cyto_any.hpp:454
void *(*)(Storage *s, void const *type) Get
Definition: cyto_any.hpp:155
void(*)(Storage *s) Drop
Definition: cyto_any.hpp:158
constexpr AnyActions(Get g, Copy c, Move m, Drop d, void const *t) noexcept
Definition: cyto_any.hpp:162
void const * type
Definition: cyto_any.hpp:171
void(*)(Storage *dst, Storage *src) Move
Definition: cyto_any.hpp:157
constexpr AnyActions() noexcept
Definition: cyto_any.hpp:160
void(*)(Storage *dst, Storage const *src) Copy
Definition: cyto_any.hpp:156
static ANY_ALWAYS_INLINE X & make(Storage *s, std::in_place_type_t< X > vtype, Args &&... args)
Definition: cyto_any.hpp:179
static constexpr AnyActions actions
Definition: cyto_any.hpp:289
StorageBuffer buf
Definition: cyto_any.hpp:122
constexpr Storage()
Definition: cyto_any.hpp:116
Storage(Storage const &)=delete
Storage & operator=(Storage const &)=delete
Storage(Storage &&)=delete
Storage & operator=(Storage &&)=delete