-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathvariant.h
1576 lines (1321 loc) · 61.4 KB
/
variant.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Implements the class template variant represents a type-safe union. An
// instance of variant at any given time either holds a value of one of its
// alternative types, or it holds no value.
//
// As with unions, if a variant holds a value of some object type T, the object
// representation of T is allocated directly within the object representation of
// the variant itself.
//
// Variant is not allowed to allocate additional (dynamic) memory.
//
// A variant is not permitted to hold references, arrays, or the type void.
// Empty variants are also ill-formed (variant<monostate> can be used instead).
//
// A variant is permitted to hold the same type more than once, and to hold
// differently cv-qualified versions of the same type. As with unions, the
// default-initialized variant holds a value of its first alternative, unless
// that alternative is not default-constructible (in which case default
// constructor won't compile: the helper class monostate can be used to make
// such variants default-constructible)
//
// Given defect 2901, the eastl::variant implementation does not provide the
// specified allocator-aware functions. This will be re-evaluated when the LWG
// addresses this issue in future standardization updates.
// LWG Defect 2901: https://cplusplus.github.io/LWG/issue2901
//
// Allocator-extended constructors
// template <class Alloc> variant(allocator_arg_t, const Alloc&);
// template <class Alloc> variant(allocator_arg_t, const Alloc&, const variant&);
// template <class Alloc> variant(allocator_arg_t, const Alloc&, variant&&);
// template <class Alloc, class T> variant(allocator_arg_t, const Alloc&, T&&);
// template <class Alloc, class T, class... Args> variant(allocator_arg_t, const Alloc&, in_place_type_t<T>, Args&&...);
// template <class Alloc, class T, class U, class... Args> variant(allocator_arg_t, const Alloc&, in_place_type_t<T>, initializer_list<U>, Args&&...);
// template <class Alloc, size_t I, class... Args> variant(allocator_arg_t, const Alloc&, in_place_index_t<I>, Args&&...);
// template <class Alloc, size_t I, class U, class... Args> variant(allocator_arg_t, const Alloc&, in_place_index_t<I>, initializer_list<U>, Args&&...);
//
// 20.7.12, allocator-related traits
// template <class T, class Alloc> struct uses_allocator;
// template <class... Types, class Alloc> struct uses_allocator<variant<Types...>, Alloc>;
//
// eastl::variant doesn't support:
// * recursive variant support
// * strong exception guarantees as specified (we punted on the assignment problem).
// if an exception is thrown during assignment its undefined behaviour in our implementation.
//
// Reference:
// * http://en.cppreference.com/w/cpp/utility/variant
// * https://thenewcpp.wordpress.com/2012/02/15/variadic-templates-part-3-or-how-i-wrote-a-variant-class/
///////////////////////////////////////////////////////////////////////////
#ifndef EASTL_VARIANT_H
#define EASTL_VARIANT_H
#include <eastl/internal/config.h>
#include <eastl/internal/in_place_t.h>
#include <eastl/internal/integer_sequence.h>
#include <eastl/meta.h>
#include <eastl/utility.h>
#include <eastl/functional.h>
#include <eastl/initializer_list.h>
#include <eastl/tuple.h>
#include <eastl/type_traits.h>
#include <eastl/array.h>
#if EASTL_EXCEPTIONS_ENABLED
#include <stdexcept>
#include <exception>
#endif
#if defined(EASTL_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
#ifndef EA_COMPILER_CPP14_ENABLED
static_assert(false, "eastl::variant requires a C++14 compatible compiler (at least) ");
#endif
EA_DISABLE_VC_WARNING(4625) // copy constructor was implicitly defined as deleted
namespace eastl
{
namespace internal
{
///////////////////////////////////////////////////////////////////////////
// default_construct_if_supported<T>
//
// Utility class to remove default constructor calls for types that
// do not support default construction.
//
// We can remove these utilities when C++17 'constexpr if' is available.
//
template<typename T, bool = eastl::is_default_constructible_v<T>>
struct default_construct_if_supported
{
static void call(T* pThis)
{
new (pThis) T();
}
};
template<typename T>
struct default_construct_if_supported<T, false>
{
static void call(T*) {} // intentionally blank
};
///////////////////////////////////////////////////////////////////////////
// destroy_if_supported<T>
//
// Utility class to remove default constructor calls for types that
// do not support default construction.
//
// We can remove these utilities when C++17 'constexpr if' is available.
//
template<typename T, bool = eastl::is_destructible_v<T>>
struct destroy_if_supported
{
static void call(T* pThis)
{
pThis->~T();
}
};
template<typename T>
struct destroy_if_supported<T, false>
{
static void call(T*) {} // intentionally blank
};
///////////////////////////////////////////////////////////////////////////
// copy_if_supported<T>
//
// Utility class to remove copy constructor calls for types that
// do not support copying.
//
// We can remove these utilities when C++17 'constexpr if' is available.
//
template<typename T, bool = eastl::is_copy_constructible_v<T>>
struct copy_if_supported
{
static void call(T* pThis, T* pOther)
{
new (pThis) T(*pOther);
}
};
template<typename T>
struct copy_if_supported<T, false>
{
static void call(T*, T*) {} // intentionally blank
};
///////////////////////////////////////////////////////////////////////////
// move_if_supported<T>
//
// Utility class to remove move constructor calls for types that
// do not support moves.
//
// We can remove these utilities when C++17 'constexpr if' is available.
//
template<typename T, bool = eastl::is_move_constructible_v<T>>
struct move_if_supported
{
static void call(T* pThis, T* pOther)
{
new (pThis) T(eastl::move(*pOther));
}
};
template<typename T>
struct move_if_supported<T, false>
{
static void call(T*, T*) {} // intentionally blank
};
} // namespace internal
///////////////////////////////////////////////////////////////////////////
// 20.7.3, variant_npos
//
EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t variant_npos = size_t(-1);
///////////////////////////////////////////////////////////////////////////
// 20.7.10, class bad_variant_access
//
#if EASTL_EXCEPTIONS_ENABLED
struct bad_variant_access : public std::logic_error
{
bad_variant_access() : std::logic_error("eastl::bad_variant_access exception") {}
virtual ~bad_variant_access() EASTL_NOEXCEPT {}
};
#endif
///////////////////////////////////////////////////////////////////////////
// TODO(rparolin): JUST COPY/PASTE THIS CODE
//
inline void CheckVariantCondition(bool b)
{
EA_UNUSED(b);
#if EASTL_EXCEPTIONS_ENABLED
if (!b)
throw bad_variant_access();
#elif EASTL_ASSERT_ENABLED
EASTL_ASSERT_MSG(b, "eastl::bad_variant_access assert");
#else
EA_UNUSED(b);
#endif
}
///////////////////////////////////////////////////////////////////////////
// 20.7.7, class monostate
//
// Unit type intended for use as a well-behaved empty alternative in
// variant. A variant of non-default-constructible types may list monostate
// as its first alternative: this makes the variant itself default-contructible.
//
struct monostate {};
// 20.7.8, monostate relational operators
#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
EA_CONSTEXPR std::strong_ordering operator<=>(monostate, monostate) EASTL_NOEXCEPT { return std::strong_ordering::equal; }
#else
EA_CONSTEXPR bool operator> (monostate, monostate) EASTL_NOEXCEPT { return false; }
EA_CONSTEXPR bool operator< (monostate, monostate) EASTL_NOEXCEPT { return false; }
EA_CONSTEXPR bool operator!=(monostate, monostate) EASTL_NOEXCEPT { return false; }
EA_CONSTEXPR bool operator<=(monostate, monostate) EASTL_NOEXCEPT { return true; }
EA_CONSTEXPR bool operator>=(monostate, monostate) EASTL_NOEXCEPT { return true; }
#endif
EA_CONSTEXPR bool operator==(monostate, monostate) EASTL_NOEXCEPT { return true; }
// 20.7.11, hash support
template <class T> struct hash;
template <> struct hash<monostate>
{ size_t operator()(monostate) const { return static_cast<size_t>(-0x42); } };
///////////////////////////////////////////////////////////////////////////
// variant_storage
//
// This is a utility class to simplify the implementation of a storage type
// for a distriminted union. This utility handles the alignment, size
// requirements, and data access required by the variant type.
//
template<bool IsTriviallyDestructible, class... Types>
struct variant_storage;
// variant_storage
//
// specialization for non-trivial types (must call constructors and destructors)
//
template<class... Types>
struct variant_storage<false, Types...>
{
enum class StorageOp
{
DEFAULT_CONSTRUCT,
DESTROY,
COPY,
MOVE
};
// handler function
using storage_handler_ptr = void(*)(StorageOp, void*, void*);
using aligned_storage_impl_t = aligned_union_t<16, Types...>;
aligned_storage_impl_t mBuffer;
storage_handler_ptr mpHandler = nullptr;
template<typename VariantStorageT>
inline void DoOp(StorageOp op, VariantStorageT&& other) // bind to both rvalue and lvalues
{
if(mpHandler)
DoOp(StorageOp::DESTROY);
if (other.mpHandler)
mpHandler = other.mpHandler;
if(mpHandler)
mpHandler(op, (void*)&mBuffer, (void*)&other.mBuffer);
}
inline void DoOp(StorageOp op)
{
if(mpHandler)
mpHandler(op, &mBuffer, nullptr);
}
template<typename T>
static void DoOpImpl(StorageOp op, T* pThis, T* pOther)
{
switch (op)
{
case StorageOp::DEFAULT_CONSTRUCT:
{
internal::default_construct_if_supported<T>::call(pThis);
}
break;
case StorageOp::DESTROY:
{
internal::destroy_if_supported<T>::call(pThis);
}
break;
case StorageOp::COPY:
{
internal::copy_if_supported<T>::call(pThis, pOther);
}
break;
case StorageOp::MOVE:
{
internal::move_if_supported<T>::call(pThis, pOther);
}
break;
default: {} break;
};
}
public:
variant_storage()
{
DoOp(StorageOp::DEFAULT_CONSTRUCT);
}
~variant_storage()
{
DoOp(StorageOp::DESTROY);
}
variant_storage(const variant_storage& other)
{
DoOp(StorageOp::COPY, other);
}
variant_storage(variant_storage&& other)
{
DoOp(StorageOp::MOVE, other);
}
variant_storage& operator=(const variant_storage& other)
{
DoOp(StorageOp::COPY, other);
return *this;
}
variant_storage& operator=(variant_storage&& other)
{
DoOp(StorageOp::MOVE, eastl::move(other));
return *this;
}
template <typename T, typename... Args>
void set_as(Args&&... args)
{
// NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which
// variant_storage used to store types. The size selected should be large enough to hold the largest type in
// the user provided variant type-list.
static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size");
using RT = remove_reference_t<T>;
new (&mBuffer) RT(eastl::forward<Args>(args)...);
mpHandler = (storage_handler_ptr)&DoOpImpl<RT>;
}
template <typename T, typename U, typename... Args>
void set_as(std::initializer_list<U> il, Args&&... args)
{
// NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which
// variant_storage used to store types. The size selected should be large enough to hold the largest type in
// the user provided variant type-list.
static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size");
using RT = remove_reference_t<T>;
new (&mBuffer) RT(il, eastl::forward<Args>(args)...);
mpHandler = (storage_handler_ptr)&DoOpImpl<RT>;
}
template<typename T>
T get_as()
{
static_assert(eastl::is_pointer_v<T>, "T must be a pointer type");
return reinterpret_cast<T>(&mBuffer);
}
template<typename T>
const T get_as() const
{
static_assert(eastl::is_pointer_v<T>, "T must be a pointer type");
return reinterpret_cast<const T>(reinterpret_cast<uintptr_t>(&mBuffer));
}
void destroy()
{
DoOp(StorageOp::DESTROY);
}
};
// variant_storage
//
// specialization for trivial types
//
template<class... Types>
struct variant_storage<true, Types...>
{
using aligned_storage_impl_t = aligned_union_t<16, Types...>;
aligned_storage_impl_t mBuffer;
public:
// NOTE(rparolin): Since this is the specialization for trivial types can we potentially remove all the
// defaulted special constructors. Consider removing this.
//
// variant_storage() = default;
// ~variant_storage() = default;
// variant_storage(const variant_storage& other) = default;
// variant_storage(variant_storage&& other) = default;
// variant_storage& operator=(const variant_storage& other) = default;
// variant_storage& operator=(variant_storage&& other) = default;
template <typename T, typename... Args>
void set_as(Args&&... args)
{
// NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which
// variant_storage used to store types. The size selected should be large enough to hold the largest type in
// the user provided variant type-list.
static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size");
new (&mBuffer) remove_reference_t<T>(eastl::forward<Args>(args)...);
// mpHandler = ...; // member does not exist in this template specialization
}
template <typename T, typename U, typename... Args>
void set_as(std::initializer_list<U> il, Args&&... args)
{
// NOTE(rparolin): If this assert fires there is an EASTL problem picking the size of the local buffer which
// variant_storage used to store types. The size selected should be large enough to hold the largest type in
// the user provided variant type-list.
static_assert(sizeof(aligned_storage_impl_t) >= sizeof(T), "T is larger than local buffer size");
new (&mBuffer) remove_reference_t<T>(il, eastl::forward<Args>(args)...);
// mpHandler = ...; // member does not exist in this template specialization
}
template<typename T>
T get_as()
{
static_assert(eastl::is_pointer_v<T>, "T must be a pointer type");
return reinterpret_cast<T>(&mBuffer);
}
template<typename T>
const T get_as() const
{
static_assert(eastl::is_pointer_v<T>, "T must be a pointer type");
return reinterpret_cast<const T>(reinterpret_cast<uintptr_t>(&mBuffer));
}
void destroy() {}
};
///////////////////////////////////////////////////////////////////////////
// 20.7.2, forward-declaration for types that depend on the variant
//
template <class... Types>
class variant;
///////////////////////////////////////////////////////////////////////////
// 20.7.3, variant_size, variant_size_v helper classes
//
template <class T> struct variant_size;
template <class T> struct variant_size<const T> : integral_constant<size_t, variant_size<T>::value> {};
template <class T> struct variant_size<volatile T> : integral_constant<size_t, variant_size<T>::value> {};
template <class T> struct variant_size<const volatile T> : integral_constant<size_t, variant_size<T>::value> {};
template <class... Types> struct variant_size<variant<Types...>> : integral_constant<size_t, sizeof...(Types)> {};
// variant_size_v template alias
template <typename T>
EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t variant_size_v = variant_size<T>::value;
///////////////////////////////////////////////////////////////////////////
// variant_alternative_helper
//
// This helper does the heavy lifting of traversing the variadic type list
// and retrieving the type at the user provided index.
//
template <size_t I, typename... Ts>
struct variant_alternative_helper;
template <size_t I, typename Head, typename... Tail>
struct variant_alternative_helper<I, Head, Tail...>
{ typedef typename variant_alternative_helper<I - 1, Tail...>::type type; };
template <typename Head, typename... Tail>
struct variant_alternative_helper<0, Head, Tail...>
{ typedef Head type; };
///////////////////////////////////////////////////////////////////////////
// 20.7.4, variant_alternative
//
template <size_t I, class T> struct variant_alternative;
template <size_t I, class... Types> struct variant_alternative<I, variant<Types...>> : variant_alternative_helper<I, Types...> {};
// ISO required cv-qualifer specializations
template <size_t I, class T> struct variant_alternative<I, const T> : add_const<typename variant_alternative<I, T>::type> {};
template <size_t I, class T> struct variant_alternative<I, volatile T> : add_volatile<typename variant_alternative<I, T>::type> {};
template <size_t I, class T> struct variant_alternative<I, const volatile T> : add_cv<typename variant_alternative<I, T>::type> {};
// variant_alternative_t template alias
template <size_t I, class T> using variant_alternative_t = typename variant_alternative<I, T>::type;
///////////////////////////////////////////////////////////////////////////
// 20.7.11, hash support
//
template <class... Types>
struct hash<variant<Types...> >
{ size_t operator()(const variant<Types...>&) const { return static_cast<size_t>(-0x42); } };
///////////////////////////////////////////////////////////////////////////
// get_if
//
template <size_t I, class... Types, enable_if_t<I < sizeof...(Types), bool> = true>
EA_CONSTEXPR add_pointer_t<variant_alternative_t<I, variant<Types...>>> get_if(variant<Types...>* pv) EASTL_NOEXCEPT
{
using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>;
return (!pv || pv->index() != I) ? nullptr : pv->mStorage.template get_as<return_type>();
}
template <size_t I, class... Types, enable_if_t<I < sizeof...(Types), bool> = true>
EA_CONSTEXPR add_pointer_t<const variant_alternative_t<I, variant<Types...>>> get_if(const variant<Types...>* pv) EASTL_NOEXCEPT
{
using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>;
return (!pv || pv->index() != I) ? nullptr : pv->mStorage.template get_as<return_type>();
}
template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>, enable_if_t<I < sizeof...(Types) && meta::duplicate_type_check_v<T, Types...>, bool> = true>
EA_CONSTEXPR add_pointer_t<T> get_if(variant<Types...>* pv) EASTL_NOEXCEPT
{
return get_if<I>(pv);
}
template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>, enable_if_t<I < sizeof...(Types) && meta::duplicate_type_check_v<T, Types...>, bool> = true>
EA_CONSTEXPR add_pointer_t<const T> get_if(const variant<Types...>* pv) EASTL_NOEXCEPT
{
return get_if<I>(pv);
}
///////////////////////////////////////////////////////////////////////////
// get
//
template <size_t I, class... Types, enable_if_t<I < sizeof...(Types), bool> = true>
EA_CONSTEXPR variant_alternative_t<I, variant<Types...>>& get(variant<Types...>& v)
{
using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>;
return *v.mStorage.template get_as<return_type>();
}
template <size_t I, class... Types, enable_if_t<I < sizeof...(Types), bool> = true>
EA_CONSTEXPR variant_alternative_t<I, variant<Types...>>&& get(variant<Types...>&& v)
{
using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>;
return eastl::move(*v.mStorage.template get_as<return_type>());
}
template <size_t I, class... Types, enable_if_t<I < sizeof...(Types), bool> = true>
EA_CONSTEXPR const variant_alternative_t<I, variant<Types...>>& get(const variant<Types...>& v)
{
using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>;
return *v.mStorage.template get_as<return_type>();
}
template <size_t I, class... Types, enable_if_t<I < sizeof...(Types), bool> = true>
EA_CONSTEXPR const variant_alternative_t<I, variant<Types...>>&& get(const variant<Types...>&& v)
{
using return_type = add_pointer_t<variant_alternative_t<I, variant<Types...>>>;
return eastl::move(*v.mStorage.template get_as<return_type>());
}
template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>, enable_if_t<I < sizeof...(Types) && meta::duplicate_type_check_v<T, Types...>, bool> = true>
EA_CONSTEXPR T& get(variant<Types...>& v)
{
return get<I>(v);
}
template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>, enable_if_t<I < sizeof...(Types) && meta::duplicate_type_check_v<T, Types...>, bool> = true>
EA_CONSTEXPR T&& get(variant<Types...>&& v)
{
return get<I>(eastl::move(v));
}
template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>, enable_if_t<I < sizeof...(Types) && meta::duplicate_type_check_v<T, Types...>, bool> = true>
EA_CONSTEXPR const T& get(const variant<Types...>& v)
{
return get<I>(v);
}
template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>, enable_if_t<I < sizeof...(Types) && meta::duplicate_type_check_v<T, Types...>, bool> = true>
EA_CONSTEXPR const T&& get(const variant<Types...>&& v)
{
return get<I>(v);
}
///////////////////////////////////////////////////////////////////////////
// 20.7.4, value access
//
template <class T, class... Types, size_t I = meta::get_type_index_v<T, Types...>, enable_if_t<I < sizeof...(Types) && meta::duplicate_type_check_v<T, Types...>, bool> = true>
EA_CONSTEXPR bool holds_alternative(const variant<Types...>& v) EASTL_NOEXCEPT
{
return I == variant_npos ? false : (v.index() == I);
}
///////////////////////////////////////////////////////////////////////////
// 20.7.2, variant
//
template <class... Types>
class variant
{
static_assert(sizeof...(Types) > 0, "variant must have at least 1 type (empty variants are ill-formed)");
static_assert(disjunction_v<is_void<Types>...> == false, "variant does not allow void as an alternative type");
static_assert(disjunction_v<is_reference<Types>...> == false, "variant does not allow references as an alternative type");
static_assert(disjunction_v<is_array<Types>...> == false, "variant does not allow arrays as an alternative type");
using variant_index_t = size_t;
using variant_storage_t = variant_storage<conjunction_v<is_trivially_destructible<Types>...>, Types...>;
using T_0 = variant_alternative_t<0, variant<Types...>>; // alias for the 1st type in the variadic pack
///////////////////////////////////////////////////////////////////////////
// variant data members
//
variant_index_t mIndex;
variant_storage_t mStorage;
public:
///////////////////////////////////////////////////////////////////////////
// 20.7.2.1, constructors
//
// Only participates in overload resolution when the first alternative is default constructible
template <typename TT0 = T_0, typename = enable_if_t<is_default_constructible_v<TT0>>>
EA_CONSTEXPR variant() EASTL_NOEXCEPT : mIndex(variant_npos), mStorage()
{
mIndex = static_cast<variant_index_t>(0);
mStorage.template set_as<T_0>();
}
// Only participates in overload resolution if is_copy_constructible_v<T_i> is true for all T_i in Types....
template <bool enable = conjunction_v<is_copy_constructible<Types>...>,
typename = enable_if_t<enable>> // add a dependent type to enable sfinae
variant(const variant& other)
{
if (this != &other)
{
mIndex = other.mIndex;
mStorage = other.mStorage;
}
}
// Only participates in overload resolution if is_move_constructible_v<T_i> is true for all T_i in Types...
template <bool enable = conjunction_v<is_move_constructible<Types>...>, typename = enable_if_t<enable>> // add a dependent type to enable sfinae
EA_CONSTEXPR variant(variant&& other) EASTL_NOEXCEPT(conjunction_v<is_move_constructible<Types>...>)
: mIndex(variant_npos), mStorage()
{
if(this != &other)
{
mIndex = other.mIndex;
mStorage = eastl::move(other.mStorage);
}
}
// Conversion constructor
template <typename T,
typename T_j = meta::overload_resolution_t<T, meta::overload_set<Types...>>,
size_t I = meta::get_type_index_v<decay_t<T_j>, Types...>,
enable_if_t<!is_same_v<decay_t<T>, variant> && I < sizeof...(Types) && meta::duplicate_type_check_v<T_j, Types...>, bool> = true>
EA_CONSTEXPR variant(T&& t) EASTL_NOEXCEPT(is_nothrow_constructible_v<T_j, T>)
: mIndex(variant_npos), mStorage()
{
mIndex = static_cast<variant_index_t>(I);
mStorage.template set_as<T_j>(eastl::forward<T>(t));
}
///////////////////////////////////////////////////////////////////////////
// 20.7.2.1, in_place_t constructors
//
template <
class T,
class... Args,
class = enable_if_t<conjunction_v<meta::duplicate_type_check<T, Types...>, is_constructible<T, Args...>>, T>>
EA_CPP14_CONSTEXPR explicit variant(in_place_type_t<T>, Args&&... args)
: variant(in_place<meta::get_type_index_v<T, Types...>>, eastl::forward<Args>(args)...)
{}
template <
class T,
class U,
class... Args,
class = enable_if_t<conjunction_v<meta::duplicate_type_check<T, Types...>, is_constructible<T, Args...>>, T>>
EA_CPP14_CONSTEXPR explicit variant(in_place_type_t<T>, std::initializer_list<U> il, Args&&... args)
: variant(in_place<meta::get_type_index_v<T, Types...>>, il, eastl::forward<Args>(args)...)
{}
template <size_t I,
class... Args,
class = enable_if_t<conjunction_v<integral_constant<bool, (I < sizeof...(Types))>,
is_constructible<meta::get_type_at_t<I, Types...>, Args...>>>>
EA_CPP14_CONSTEXPR explicit variant(in_place_index_t<I>, Args&&... args)
: mIndex(I)
{
mStorage.template set_as<meta::get_type_at_t<I, Types...>>(eastl::forward<Args>(args)...);
}
template <size_t I,
class U,
class... Args,
class = enable_if_t<conjunction_v<integral_constant<bool, (I < sizeof...(Types))>,
is_constructible<meta::get_type_at_t<I, Types...>, Args...>>>>
EA_CPP14_CONSTEXPR explicit variant(in_place_index_t<I>, std::initializer_list<U> il, Args&&... args)
: mIndex(I)
{
mStorage.template set_as<meta::get_type_at_t<I, Types...>>(il, eastl::forward<Args>(args)...);
}
///////////////////////////////////////////////////////////////////////////
// 20.7.2.2, destructor
//
~variant() = default;
///////////////////////////////////////////////////////////////////////////
// 20.7.2.4, modifiers
//
// Equivalent to emplace<I>(std::forward<Args>(args)...), where I is the zero-based index of T in Types....
// This overload only participates in overload resolution if std::is_constructible_v<T, Args...> is true, and T
// occurs exactly once in Types...
template <
class T,
class... Args,
size_t I = meta::get_type_index_v<T, Types...>,
typename = enable_if_t<conjunction_v<is_constructible<T, Args...>, meta::duplicate_type_check<T, Types...>>>>
decltype(auto) emplace(Args&&... args)
{
return emplace<I>(eastl::forward<Args>(args)...);
}
// Equivalent to emplace<I>(il, std::forward<Args>(args)...), where I is the zero-based index of T in Types....
// This overload only participates in overload resolution if std::is_constructible_v<T,
// std::initializer_list<U>&, Args...> is true, and T occurs exactly once in Types...
template <class T,
class U,
class... Args,
size_t I = meta::get_type_index_v<T, Types...>,
typename = enable_if_t<conjunction_v<is_constructible<T, std::initializer_list<U>&, Args...>,
meta::duplicate_type_check<T, Types...>>>>
decltype(auto) emplace(std::initializer_list<U> il, Args&&... args)
{
return emplace<I>(il, eastl::forward<T>(args)...);
}
// First, destroys the currently contained value (if any). Then direct-initializes the contained value as if
// constructing a value of type T_I with the arguments std::forward<Args>(args).... If an exception is thrown,
// *this may become valueless_by_exception. This overload only participates in overload resolution if
// std::is_constructible_v<T_I, Args...> is true. The behavior is undefined if I is not less than
// sizeof...(Types).
//
EA_DISABLE_VC_WARNING(4702) // unreachable code: suppress warning because set_as<T>() may always throws (because T(args...) throws).
template <size_t I,
class... Args,
typename T = meta::get_type_at_t<I, Types...>,
typename =
enable_if_t<conjunction_v<is_constructible<T, Args...>, meta::duplicate_type_check<T, Types...>>>>
variant_alternative_t<I, variant>& emplace(Args&&... args)
{
if (!valueless_by_exception())
{
mStorage.destroy();
#if EASTL_EXCEPTIONS_ENABLED
mIndex = static_cast<variant_index_t>(variant_npos);
#endif
}
mStorage.template set_as<T>(eastl::forward<Args>(args)...);
mIndex = static_cast<variant_index_t>(I);
return *reinterpret_cast<T*>(&mStorage.mBuffer);
}
EA_RESTORE_VC_WARNING()
// First, destroys the currently contained value (if any). Then direct-initializes the contained value as if
// constructing a value of type T_I with the arguments il, std::forward<Args>(args).... If an exception is
// thrown, *this may become valueless_by_exception. This overload only participates in overload resolution if
// std::is_constructible_v<T_I, initializer_list<U>&, Args...> is true. The behavior is undefined if I is not
// less than sizeof...(Types).
//
EA_DISABLE_VC_WARNING(4702) // unreachable code: suppress warning because set_as<T>() may always throws (because T(args...) throws).
template <size_t I,
class U,
class... Args,
typename T = meta::get_type_at_t<I, Types...>,
typename = enable_if_t<conjunction_v<is_constructible<T, std::initializer_list<U>&, Args...>, meta::duplicate_type_check<T, Types...>>>>
variant_alternative_t<I, variant>& emplace(std::initializer_list<U> il, Args&&... args)
{
if (!valueless_by_exception())
{
mStorage.destroy();
#if EASTL_EXCEPTIONS_ENABLED
mIndex = static_cast<variant_index_t>(variant_npos);
#endif
}
mStorage.template set_as<T>(il, eastl::forward<Args>(args)...);
mIndex = static_cast<variant_index_t>(I);
return *reinterpret_cast<T*>(&mStorage.mBuffer);
}
EA_RESTORE_VC_WARNING()
///////////////////////////////////////////////////////////////////////////
// 20.7.2.3, assignment
//
template <class T,
typename T_j = meta::overload_resolution_t<T, meta::overload_set<Types...>>,
size_t I = meta::get_type_index_v<decay_t<T_j>, Types...>,
enable_if_t<!eastl::is_same_v<decay_t<T>, variant> && eastl::is_assignable_v<T_j&, T> &&
eastl::is_constructible_v<T_j, T> && I < sizeof...(Types) && meta::duplicate_type_check_v<T_j, Types...>, bool> = true>
EA_CPP14_CONSTEXPR variant& operator=(T&& t)
EASTL_NOEXCEPT(conjunction_v<is_nothrow_assignable<T_j&, T>, is_nothrow_constructible<T_j, T>>)
{
if (!valueless_by_exception())
mStorage.destroy();
mIndex = static_cast<variant_index_t>(I);
mStorage.template set_as<T_j>(eastl::forward<T>(t));
return *this;
}
// Only participates in overload resolution if is_copy_constructible_v<T_i> && is_copy_assignable_v<T_i> is true
// for all T_i in Types....
template <bool enable = conjunction_v<conjunction<is_copy_constructible<Types>...>,
conjunction<is_copy_assignable<Types>...>>,
typename = enable_if_t<enable>> // add a dependent type to enable sfinae
variant& operator=(const variant& other)
{
if (this != &other)
{
mIndex = other.mIndex;
mStorage = other.mStorage;
}
return *this;
}
// Only participates in overload resolution if is_move_constructible_v<T_i> && is_move_assignable_v<T_i> is true for all T_i in Types....
template <bool enable = conjunction_v<conjunction<is_move_constructible<Types>...>,
conjunction<is_move_assignable<Types>...>>,
typename = enable_if_t<enable>> // add a dependent type to enable sfinae
variant& operator=(variant&& other)
EASTL_NOEXCEPT(conjunction_v<conjunction<is_nothrow_move_constructible<Types>...>,
conjunction<is_nothrow_move_assignable<Types>...>>)
{
if (this != &other)
{
mIndex = eastl::move(other.mIndex);
mStorage = eastl::move(other.mStorage);
}
return *this;
}
///////////////////////////////////////////////////////////////////////////
// 20.7.2.5, value status
//
EA_CONSTEXPR size_t index() const EASTL_NOEXCEPT
{
#if EASTL_EXCEPTIONS_ENABLED
return valueless_by_exception() ? variant_npos : mIndex;
#else
return mIndex;
#endif
}
EA_CONSTEXPR bool valueless_by_exception() const EASTL_NOEXCEPT
{
#if EASTL_EXCEPTIONS_ENABLED
return mIndex == variant_npos;
#else
return false;
#endif
}
///////////////////////////////////////////////////////////////////////////
// 20.7.2.6, swap
//
void swap(variant& other)
EASTL_NOEXCEPT(conjunction_v<is_nothrow_move_constructible<Types>..., is_nothrow_swappable<Types>...>)
{
eastl::swap(mIndex, other.mIndex);
eastl::swap(mStorage, other.mStorage);
}
private:
// NOTE(rparolin): get_if accessors require internal access to the variant storage class
template <size_t I, class... Types2, enable_if_t<I < sizeof...(Types2), bool>> friend EA_CONSTEXPR add_pointer_t< variant_alternative_t<I, variant<Types2...>>> get_if( variant<Types2...>* pv) EASTL_NOEXCEPT;
template <size_t I, class... Types2, enable_if_t<I < sizeof...(Types2), bool>> friend EA_CONSTEXPR add_pointer_t<const variant_alternative_t<I, variant<Types2...>>> get_if(const variant<Types2...>* pv) EASTL_NOEXCEPT;
// NOTE(rparolin): get accessors require internal access to the variant storage class
template <size_t I, class... Types2, enable_if_t<I < sizeof...(Types2), bool>> friend EA_CONSTEXPR variant_alternative_t<I, variant<Types2...>>& get(variant<Types2...>& v);
template <size_t I, class... Types2, enable_if_t<I < sizeof...(Types2), bool>> friend EA_CONSTEXPR variant_alternative_t<I, variant<Types2...>>&& get(variant<Types2...>&& v);
template <size_t I, class... Types2, enable_if_t<I < sizeof...(Types2), bool>> friend EA_CONSTEXPR const variant_alternative_t<I, variant<Types2...>>& get(const variant<Types2...>& v);
template <size_t I, class... Types2, enable_if_t<I < sizeof...(Types2), bool>> friend EA_CONSTEXPR const variant_alternative_t<I, variant<Types2...>>&& get(const variant<Types2...>&& v);
};
///////////////////////////////////////////////////////////////////////////
// 20.7.9, swap
//
template <class... Types>
void swap(variant<Types...>& lhs, variant<Types...>& rhs)
EASTL_NOEXCEPT(EASTL_NOEXCEPT(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
// visit is a bit convoluted, in order to fulfill a few requirements:
// - It must support visiting multiple variants using a single visitor and a single function call. The
// visitor in this case should have one function for each possible combination of types:
//
// struct MyVisitor {
// void operator()(int, int);
// void operator()(string, string);
// void operator()(int, string);
// void operator()(string, int);
// };
//
// variant<int, string> a = 42;
// variant<int, string> b = "hello";
// visit(MyVisitor{}, a, b); // calls MyVisitor::operator()(int, string)
//
// - It must be declared constexpr
// - It must be constant-time for the case of visiting a single variant
//
// - 20.7.7 states that variant visitation requires all combinations of visitors to return the same type.
//
// NOTE(mwinkler):
// Visit creates an N-Dimensional matrix whereby each dimension is M wide.
// Where N == sizeof...(Variants) and M == variant_size_v<Variant>
//
// variant<int, bool, float> v;
// visit(Visitor{}, v, v);
//
// This creates a 3x3 matrix of potential visitors.
// The argument indices into the variants are as follows.
// [0, 0], [0, 1], [0, 2]
// [1, 0], [1, 1], [1, 2]
// [2, 0], [2, 1], [2, 2]
//
// These indices are compile-time constants but the variants have a runtime index.
// Therefore we must instantiate an NxNxN... matrix of function pointers who are
// templated on the indices based on their position in the matrix and then
// at runtime index into the array to call the correct function pointer that can
// get the correct alternatives in the variants.
//
// There are a couple of ways to do this. We can construct the matrix bottom up or top down.
//
// Constructing a matrix bottom up would look something as follows.