Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
TVariant.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "TVariantMeta.h"
6#include "Templates/EnableIf.h"
7#include "Templates/IsConstructible.h"
8#include "Templates/Decay.h"
9
10/**
11 * A special tag used to indicate that in-place construction of a variant should take place.
12 */
13template <typename T>
14struct TInPlaceType {};
15
16/**
17 * A special tag that can be used as the first type in a TVariant parameter pack if none of the other types can be default-constructed.
18 */
20{
21 /** Allow FEmptyVariantState to be used with FArchive serialization */
22 friend inline FArchive& operator<<(FArchive& Ar, FEmptyVariantState&)
23 {
24 return Ar;
25 }
26};
27
28/**
29 * A type-safe union based loosely on std::variant. This flavor of variant requires that all the types in the declaring template parameter pack be unique.
30 * Attempting to use the value of a Get() when the underlying type is different leads to undefined behavior.
31 */
32template <typename T, typename... Ts>
33class TVariant final : private UE::Core::Private::TVariantStorage<T, Ts...>
34{
35 static_assert(!UE::Core::Private::TTypePackContainsDuplicates<T, Ts...>::Value, "All the types used in TVariant should be unique");
36 static_assert(!UE::Core::Private::TContainsReferenceType<T, Ts...>::Value, "TVariant cannot hold reference types");
37
38 // Test for 255 here, because the parameter pack doesn't include the initial T
39 static_assert(sizeof...(Ts) <= 255, "TVariant cannot hold more than 256 types");
40
41public:
42 /** Default initialize the TVariant to the first type in the parameter pack */
44 {
45 static_assert(TIsConstructible<T>::Value, "To default-initialize a TVariant, the first type in the parameter pack must be default constructible. Use FEmptyVariantState as the first type if none of the other types can be listed first.");
46 new(&UE::Core::Private::CastToStorage(*this).Storage) T();
47 TypeIndex = 0;
48 }
49
50 /** Perform in-place construction of a type into the variant */
51 template <typename U, typename... TArgs>
52 explicit TVariant(TInPlaceType<U>&&, TArgs&&... Args)
53 {
54 constexpr SIZE_T Index = UE::Core::Private::TParameterPackTypeIndex<U, T, Ts...>::Value;
55 static_assert(Index != (SIZE_T)-1, "The TVariant is not declared to hold the type being constructed");
56
57 new(&UE::Core::Private::CastToStorage(*this).Storage) U(Forward<TArgs>(Args)...);
58 TypeIndex = (uint8)Index;
59 }
60
61 /** Copy construct the variant from another variant of the same type */
62 TVariant(const TVariant& Other)
63 : TypeIndex(Other.TypeIndex)
64 {
65 UE::Core::Private::TCopyConstructorLookup<T, Ts...>::Construct(TypeIndex, &UE::Core::Private::CastToStorage(*this).Storage, &UE::Core::Private::CastToStorage(Other).Storage);
66 }
67
68 /** Move construct the variant from another variant of the same type */
69 TVariant(TVariant&& Other)
70 : TypeIndex(Other.TypeIndex)
71 {
72 UE::Core::Private::TMoveConstructorLookup<T, Ts...>::Construct(TypeIndex, &UE::Core::Private::CastToStorage(*this).Storage, &UE::Core::Private::CastToStorage(Other).Storage);
73 }
74
75 /** Copy assign a variant from another variant of the same type */
76 TVariant& operator=(const TVariant& Other)
77 {
78 if (&Other != this)
79 {
80 TVariant Temp = Other;
81 Swap(Temp, *this);
82 }
83 return *this;
84 }
85
86 /** Move assign a variant from another variant of the same type */
87 TVariant& operator=(TVariant&& Other)
88 {
89 if (&Other != this)
90 {
91 TVariant Temp = MoveTemp(Other);
92 Swap(Temp, *this);
93 }
94 return *this;
95 }
96
97 /** Destruct the underlying type (if appropriate) */
99 {
100 UE::Core::Private::TDestructorLookup<T, Ts...>::Destruct(TypeIndex, &UE::Core::Private::CastToStorage(*this).Storage);
101 }
102
103 /** Determine if the variant holds the specific type */
104 template <typename U>
105 bool IsType() const
106 {
107 static_assert(UE::Core::Private::TParameterPackTypeIndex<U, T, Ts...>::Value != (SIZE_T)-1, "The TVariant is not declared to hold the type passed to IsType<>");
108 return UE::Core::Private::TIsType<U, T, Ts...>::IsSame(TypeIndex);
109 }
110
111 /** Get a reference to the held value. Bad things can happen if this is called on a variant that does not hold the type asked for */
112 template <typename U>
113 U& Get()
114 {
115 constexpr SIZE_T Index = UE::Core::Private::TParameterPackTypeIndex<U, T, Ts...>::Value;
116 static_assert(Index != (SIZE_T)-1, "The TVariant is not declared to hold the type passed to Get<>");
117
118 check(Index == TypeIndex);
119 // The intermediate step of casting to void* is used to avoid warnings due to use of reinterpret_cast between related types if U and the storage class are related
120 // This was specifically encountered when U derives from TAlignedBytes
121 return *reinterpret_cast<U*>(reinterpret_cast<void*>(&UE::Core::Private::CastToStorage(*this).Storage));
122 }
123
124 /** Get a reference to the held value. Bad things can happen if this is called on a variant that does not hold the type asked for */
125 template <typename U>
126 const U& Get() const
127 {
128 // Temporarily remove the const qualifier so we can implement Get in one location.
129 return const_cast<TVariant*>(this)->template Get<U>();
130 }
131
132 /** Get a pointer to the held value if the held type is the same as the one specified */
133 template <typename U>
135 {
136 constexpr SIZE_T Index = UE::Core::Private::TParameterPackTypeIndex<U, T, Ts...>::Value;
137 static_assert(Index != (SIZE_T)-1, "The TVariant is not declared to hold the type passed to TryGet<>");
138 // The intermediate step of casting to void* is used to avoid warnings due to use of reinterpret_cast between related types if U and the storage class are related
139 // This was specifically encountered when U derives from TAlignedBytes
140 return Index == (SIZE_T)TypeIndex ? reinterpret_cast<U*>(reinterpret_cast<void*>(&UE::Core::Private::CastToStorage(*this).Storage)) : nullptr;
141 }
142
143 /** Get a pointer to the held value if the held type is the same as the one specified */
144 template <typename U>
145 const U* TryGet() const
146 {
147 // Temporarily remove the const qualifier so we can implement TryGet in one location.
148 return const_cast<TVariant*>(this)->template TryGet<U>();
149 }
150
151 /** Set a specifically-typed value into the variant */
152 template <typename U>
153 void Set(typename TIdentity<U>::Type&& Value)
154 {
155 Emplace<U>(MoveTemp(Value));
156 }
157
158 /** Set a specifically-typed value into the variant */
159 template <typename U>
160 void Set(const typename TIdentity<U>::Type& Value)
161 {
162 Emplace<U>(Value);
163 }
164
165 /** Set a specifically-typed value into the variant using in-place construction */
166 template <typename U, typename... TArgs>
167 void Emplace(TArgs&&... Args)
168 {
169 constexpr SIZE_T Index = UE::Core::Private::TParameterPackTypeIndex<U, T, Ts...>::Value;
170 static_assert(Index != (SIZE_T)-1, "The TVariant is not declared to hold the type passed to Emplace<>");
171
172 UE::Core::Private::TDestructorLookup<T, Ts...>::Destruct(TypeIndex, &UE::Core::Private::CastToStorage(*this).Storage);
173 new(&UE::Core::Private::CastToStorage(*this).Storage) U(Forward<TArgs>(Args)...);
174 TypeIndex = (uint8)Index;
175 }
176
177 /** Lookup the index of a type in the template parameter pack at compile time. */
178 template <typename U>
179 static constexpr SIZE_T IndexOfType()
180 {
181 constexpr SIZE_T Index = UE::Core::Private::TParameterPackTypeIndex<U, T, Ts...>::Value;
182 static_assert(Index != (SIZE_T)-1, "The TVariant is not declared to hold the type passed to IndexOfType<>");
183 return Index;
184 }
185
186 /** Returns the currently held type's index into the template parameter pack */
187 SIZE_T GetIndex() const
188 {
189 return (SIZE_T)TypeIndex;
190 }
191
192private:
193 /** Index into the template parameter pack for the type held. */
195};
196
197/** Determine if a type is a variant */
198template <typename T>
200{
201 static constexpr bool Value = false;
202};
203
204template <typename... Ts>
205struct TIsVariant<TVariant<Ts...>>
206{
207 static constexpr bool Value = true;
208};
209
210template <typename T> struct TIsVariant<T&> : public TIsVariant<T> {};
211template <typename T> struct TIsVariant<T&&> : public TIsVariant<T> {};
212template <typename T> struct TIsVariant<const T> : public TIsVariant<T> {};
213
214/** Determine the number of types in a TVariant */
215template <typename> struct TVariantSize;
216
217template <typename... Ts>
218struct TVariantSize<TVariant<Ts...>>
219{
220 static constexpr SIZE_T Value = sizeof...(Ts);
221};
222
223template <typename T> struct TVariantSize<T&> : public TVariantSize<T> {};
224template <typename T> struct TVariantSize<T&&> : public TVariantSize<T> {};
225template <typename T> struct TVariantSize<const T> : public TVariantSize<T> {};
226
227/** Apply a visitor function to the list of variants */
228template <
229 typename Func,
230 typename... Variants,
231 typename = typename TEnableIf<UE::Core::Private::TIsAllVariant<typename TDecay<Variants>::Type...>::Value>::Type
232>
233decltype(auto) Visit(Func&& Callable, Variants&&... Args)
234{
236 constexpr SIZE_T NumPermutations = (1 * ... * (TVariantSize<Variants>::Value));
237#else
238 constexpr SIZE_T VariantSizes[] = { TVariantSize<Variants>::Value... };
239 constexpr SIZE_T NumPermutations = UE::Core::Private::Multiply(VariantSizes, sizeof...(Variants));
240#endif
241
242 return UE::Core::Private::VisitImpl(
243 UE::Core::Private::EncodeIndices(Args...),
244 Forward<Func>(Callable),
245 TMakeIntegerSequence<SIZE_T, NumPermutations>{},
246 TMakeIntegerSequence<SIZE_T, sizeof...(Variants)>{},
247 Forward<Variants>(Args)...
248 );
249}
250
251/**
252 * Serialization function for TVariants.
253 *
254 * In order for a TVariant to be serializable, each type in its template parameter pack must:
255 * 1. Have a default constructor. This is required because when reading the type from an archive, it must be default constructed before being loaded.
256 * 2. Implement the `FArchive& operator<<(FArchive&, T&)` function. This is required to serialize the actual type that's stored in TVariant.
257 */
258template <typename... Ts>
259inline FArchive& operator<<(typename UE::Core::Private::TAlwaysFArchive<TVariant<Ts...>>::Type& Ar, TVariant<Ts...>& Variant)
260{
261 if (Ar.IsLoading())
262 {
263 uint8 Index;
264 Ar << Index;
265 check(Index < sizeof...(Ts));
266
267 UE::Core::Private::TVariantLoadFromArchiveLookup<Ts...>::Load((SIZE_T)Index, Ar, Variant);
268 }
269 else
270 {
271 uint8 Index = (uint8)Variant.GetIndex();
272 Ar << Index;
273 Visit([&Ar](auto& StoredValue)
274 {
275 Ar << StoredValue;
276 }, Variant);
277 }
278 return Ar;
279}
#define check(expr)
#define PLATFORM_COMPILER_HAS_FOLD_EXPRESSIONS
Definition Platform.h:247
decltype(auto) Visit(Func &&Callable, Variants &&... Args)
Definition TVariant.h:233
TVariant(TInPlaceType< U > &&, TArgs &&... Args)
Definition TVariant.h:52
static constexpr SIZE_T IndexOfType()
Definition TVariant.h:179
TVariant(const TVariant &Other)
Definition TVariant.h:62
U & Get()
Definition TVariant.h:113
const U * TryGet() const
Definition TVariant.h:145
TVariant()
Definition TVariant.h:43
TVariant(TVariant &&Other)
Definition TVariant.h:69
void Set(typename TIdentity< U >::Type &&Value)
Definition TVariant.h:153
void Set(const typename TIdentity< U >::Type &Value)
Definition TVariant.h:160
uint8 TypeIndex
Definition TVariant.h:194
TVariant & operator=(const TVariant &Other)
Definition TVariant.h:76
bool IsType() const
Definition TVariant.h:105
U * TryGet()
Definition TVariant.h:134
SIZE_T GetIndex() const
Definition TVariant.h:187
TVariant & operator=(TVariant &&Other)
Definition TVariant.h:87
void Emplace(TArgs &&... Args)
Definition TVariant.h:167
const U & Get() const
Definition TVariant.h:126
~TVariant()
Definition TVariant.h:98
constexpr SIZE_T Multiply(const SIZE_T *Args, SIZE_T Num)
Definition Vector.h:40
static constexpr bool Value
Definition TVariant.h:207
static constexpr bool Value
Definition TVariant.h:201
static constexpr SIZE_T Value
Definition TVariant.h:220