Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
TaggedUnion.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../../Libraries/Foundation/AlignedStorage.h"
5#include "../../Libraries/Foundation/InitializerList.h"
6#include "TypeList.h"
7namespace SC
8{
9template <typename Tag>
10struct TaggedUnion;
11template <typename EnumType, EnumType enumValue, typename MemberType>
12struct TaggedType;
13} // namespace SC
14
18
23template <typename EnumType, EnumType enumValue, typename MemberType>
25{
26 using type = MemberType;
27
28 static constexpr EnumType value = enumValue;
29};
30
36template <typename Union>
38{
39 private:
40 template <int Index>
42
43 public:
44 static constexpr auto NumTypes = Union::FieldsTypes::size;
45
47 {
48 type = TypeAt<0>::value;
49 new (&fieldAt<0>(), PlacementNew()) typename TypeAt<0>::type();
50 }
51
53 ~TaggedUnion() { visit(Destruct()); }
54
58 {
59 type = other.type;
60 visit(CopyConstruct(), other);
61 }
62
66 {
67 type = other.type;
68 visit(MoveConstruct(), other);
69 }
70
74 {
75 if (type == other.type)
76 {
77 visit<CopyAssign>(other);
78 }
79 else
80 {
81 visit(Destruct());
82 type = other.type;
83 visit(CopyConstruct(), other);
84 }
85 return *this;
86 }
87
91 {
92 if (type == other.type)
93 {
94 visit(MoveAssign(), other);
95 }
96 else
97 {
98 visit(Destruct());
99 type = other.type;
100 visit(MoveConstruct(), other);
101 }
102 return *this;
103 }
104
105 using EnumType = typename TypeTraits::RemoveConst<decltype(TypeAt<0>::value)>::type;
106#if !DOXYGEN
108 template <EnumType wantedEnum, int StartIndex = NumTypes>
109 struct EnumToType
110 {
111 static constexpr int index = wantedEnum == TypeAt<StartIndex - 1>::value
112 ? StartIndex - 1
113 : EnumToType<wantedEnum, StartIndex - 1>::index;
114 static_assert(index >= 0, "Type not found!");
115 using type = TypeTraits::ConditionalT<wantedEnum == TypeAt<StartIndex - 1>::value, // Condition
116 typename TypeAt<StartIndex - 1>::type, // True
117 typename EnumToType<wantedEnum, StartIndex - 1>::type>; // False
118 };
119
120 template <EnumType wantedEnum>
121 struct EnumToType<wantedEnum, 0>
122 {
123 using type = typename TypeAt<0>::type;
124 static constexpr int index = 0;
125 };
126#endif
127
129 EnumType getType() const { return type; }
130
132 void setType(EnumType newType)
133 {
134 if (newType != type)
135 {
136 visit(Destruct());
137 type = newType;
138 visit(Construct());
139 }
140 }
141
142 bool operator==(const TaggedUnion& other) const { return type == other.type and visit<Equals>(other); }
143
148 template <EnumType wantedType, typename U>
149 void assign(U&& other)
150 {
151 auto& field = fieldAt<EnumToType<wantedType>::index>();
152 if (type == wantedType)
153 {
154 field = forward<U>(other);
155 }
156 else
157 {
158 using T = typename EnumToType<wantedType>::type;
159 visit(Destruct());
160 type = wantedType;
161 new (&field, PlacementNew()) T(forward<U>(other));
162 }
163 }
164
170 template <EnumType wantedType>
171 typename TypeAt<EnumToType<wantedType>::index>::type& changeTo()
172 {
173 using T = typename EnumToType<wantedType>::type;
174 auto& field = fieldAt<EnumToType<wantedType>::index>();
175 if (type != wantedType)
176 {
177 visit(Destruct());
178 type = wantedType;
179 new (&field, PlacementNew()) T();
180 }
181 return field;
182 }
183
188 template <EnumType wantedType>
189 [[nodiscard]] typename TypeAt<EnumToType<wantedType>::index>::type* field()
190 {
191 if (wantedType == type)
192 {
193 return &fieldAt<EnumToType<wantedType>::index>();
194 }
195 return nullptr;
196 }
197
202 template <EnumType wantedType>
203 [[nodiscard]] const typename TypeAt<EnumToType<wantedType>::index>::type* field() const
204 {
205 if (wantedType == type)
206 {
207 return &fieldAt<EnumToType<wantedType>::index>();
208 }
209 return nullptr;
210 }
211
212 private:
213 struct Destruct
214 {
215 template <int Index>
216 void operator()(TaggedUnion& t1)
217 {
218 using T = typename TypeAt<Index>::type;
219 t1.fieldAt<Index>().~T();
220 }
221 };
222
223 struct Construct
224 {
225 template <int Index>
226 void operator()(TaggedUnion& t1)
227 {
228 using T = typename TypeAt<Index>::type;
229 new (&t1.fieldAt<Index>(), PlacementNew()) T();
230 }
231 };
232
233 struct CopyConstruct
234 {
235 template <int Index>
236 void operator()(TaggedUnion& t1, const TaggedUnion& t2)
237 {
238 using T = typename TypeAt<Index>::type;
239 new (&t1.fieldAt<Index>(), PlacementNew()) T(t2.fieldAt<Index>());
240 }
241 };
242
243 struct MoveConstruct
244 {
245 template <int Index>
246 void operator()(TaggedUnion& t1, TaggedUnion& t2)
247 {
248 using T = typename TypeAt<Index>::type;
249 new (&t1.fieldAt<Index>(), PlacementNew()) T(move(t2.fieldAt<Index>()));
250 }
251 };
252
253 struct CopyAssign
254 {
255 template <int Index>
256 void operator()(TaggedUnion& t1, const TaggedUnion& t2)
257 {
258 t1.fieldAt<Index>() = t2.fieldAt<Index>();
259 }
260 };
261
262 struct MoveAssign
263 {
264 template <int Index>
265 void operator()(TaggedUnion& t1, TaggedUnion& t2)
266 {
267 t1.fieldAt<Index>() = move(t2.fieldAt<Index>());
268 }
269 };
270
271 struct Equals
272 {
273 template <int Index>
274 static auto visit(TaggedUnion& t1, TaggedUnion& t2)
275 {
276 return t1.fieldAt<Index>() == move(t2.fieldAt<Index>());
277 }
278 };
279
280 template <typename Visitor, typename... Arguments>
281 auto visit(Visitor&& visitor, Arguments&&... args)
282 {
283 return RuntimeEnumVisit<Visitor>::visit(forward<Visitor>(visitor), type, *this, forward<Arguments>(args)...);
284 }
285
286 template <typename Visitor, size_t StartIndex = NumTypes>
287 struct RuntimeEnumVisit
288 {
289 static constexpr auto Index = StartIndex - 1;
290
291 template <typename... Args>
292 static auto visit(Visitor&& visitor, EnumType enumType, Args&... args)
293 {
294 if (enumType == TypeAt<Index>::value)
295 {
296 return visitor.template operator()<Index>(args...);
297 }
298 else
299 {
300 return RuntimeEnumVisit<Visitor, Index>::visit(forward<Visitor>(visitor), enumType, args...);
301 }
302 }
303 };
304
305 template <typename Visitor>
306 struct RuntimeEnumVisit<Visitor, 0>
307 {
308 template <typename... Args>
309 static auto visit(Visitor&& visitor, EnumType, Args&... args)
310 {
311 return visitor.template operator()<0>(args...);
312 }
313 };
314
315 template <int index>
316 [[nodiscard]] auto& fieldAt()
317 {
318 using T = typename TypeAt<index>::type;
319 return storage.template reinterpret_as<T>();
320 }
321
322 template <int index>
323 [[nodiscard]] const auto& fieldAt() const
324 {
325 using T = typename TypeAt<index>::type;
326 return storage.template reinterpret_as<const T>();
327 }
328
329 template <class ForwardIt>
330 static constexpr ForwardIt MaxElement(ForwardIt first, ForwardIt last)
331 {
332 if (first == last)
333 return last;
334 ForwardIt largest = first;
335 for (++first; first != last; ++first)
336 {
337 if (*largest < *first)
338 largest = first;
339 }
340
341 return largest;
342 }
343
344 template <class T>
345 static constexpr T MaxElement(std::initializer_list<T> list)
346 {
347 return *MaxElement(list.begin(), list.end());
348 }
349
350 template <class... Types>
351 struct ComputeMaxSizeAndAlignment;
352
353 template <class... Types>
354 struct ComputeMaxSizeAndAlignment<TypeTraits::TypeList<Types...>>
355 {
356 static constexpr size_t maxAlignment = MaxElement({alignof(typename Types::type)...});
357 static constexpr size_t maxSize = MaxElement({sizeof(typename Types::type)...});
358 };
359
360 using Storage = ComputeMaxSizeAndAlignment<typename Union::FieldsTypes>;
361
362 AlignedStorage<Storage::maxSize, Storage::maxAlignment> storage;
363
364 EnumType type;
365};
typename TypeListGet< T, N >::type TypeListGetT
Alias template to simplify accessing the retrieved type using TypeListGet.
Definition TypeList.h:69
typename Conditional< B, T, F >::type ConditionalT
ConditionalT is an alias template that resolves to type T if a boolean value is true,...
Definition TypeTraits.h:70
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition Compiler.h:269
constexpr T && forward(typename TypeTraits::RemoveReference< T >::type &value)
Forwards an lvalue or an rvalue as an rvalue reference.
Definition Compiler.h:272
Associate a Type to an Enum, as required by TaggedUnion.
Definition TaggedUnion.h:25
Type safe union with an enum type, where each type has an associated enum value.
Definition TaggedUnion.h:38
TaggedUnion(const TaggedUnion &other)
Copy constructor.
Definition TaggedUnion.h:57
void setType(EnumType newType)
Sets the currently active type at runtime, destructing and (default) constructing the new type.
Definition TaggedUnion.h:132
TaggedUnion & operator=(TaggedUnion &&other)
Move assignment operator.
Definition TaggedUnion.h:90
EnumType getType() const
Returns enumeration value of currently active union type.
Definition TaggedUnion.h:129
const TypeAt< EnumToType< wantedType >::index >::type * field() const
Get a pointer to currently active field.
Definition TaggedUnion.h:203
TaggedUnion & operator=(const TaggedUnion &other)
Copy assignment operator.
Definition TaggedUnion.h:73
TypeAt< EnumToType< wantedType >::index >::type * field()
Get a pointer to currently active field.
Definition TaggedUnion.h:189
void assign(U &&other)
Assigns a compile time known enum type with an object U.
Definition TaggedUnion.h:149
~TaggedUnion()
Destroys the TaggedUnion object.
Definition TaggedUnion.h:53
TypeAt< EnumToType< wantedType >::index >::type & changeTo()
Changes current active type in union to a different one.
Definition TaggedUnion.h:171
TaggedUnion(TaggedUnion &&other)
Move constructor.
Definition TaggedUnion.h:65
RemoveConst removes the const qualification from a type T.
Definition TypeTraits.h:46