Sane C++ Libraries
C++ Platform Abstraction Libraries
TaggedUnion.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Foundation/AlignedStorage.h"
5#include "../Foundation/InitializerList.h"
6#include "../Foundation/TypeList.h"
7namespace SC
8{
9template <typename Tag>
10struct TaggedUnion;
11template <typename EnumType, EnumType enumValue, typename MemberType>
12struct TaggedType;
13} // namespace SC
14
17
22template <typename EnumType, EnumType enumValue, typename MemberType>
24{
25 using type = MemberType;
26 using enumType = EnumType;
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 TypeAt<0>::enumType;
106
108 template <EnumType wantedEnum, int StartIndex = NumTypes>
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
128 EnumType getType() const { return type; }
129
131 void setType(EnumType newType)
132 {
133 if (newType != type)
134 {
135 visit(Destruct());
136 type = newType;
137 visit(Construct());
138 }
139 }
140
141 bool operator==(const TaggedUnion& other) const { return type == other.type and visit<Equals>(other); }
142
147 template <EnumType wantedType, typename U>
148 void assign(U&& other)
149 {
150 auto& field = fieldAt<EnumToType<wantedType>::index>();
151 if (type == wantedType)
152 {
153 field = forward<U>(other);
154 }
155 else
156 {
157 using T = typename EnumToType<wantedType>::type;
158 visit(Destruct());
159 type = wantedType;
160 new (&field, PlacementNew()) T(forward<U>(other));
161 }
162 }
163
169 template <EnumType wantedType>
170 typename TypeAt<EnumToType<wantedType>::index>::type& changeTo()
171 {
172 using T = typename EnumToType<wantedType>::type;
173 auto& field = fieldAt<EnumToType<wantedType>::index>();
174 if (type != wantedType)
175 {
176 visit(Destruct());
177 type = wantedType;
178 new (&field, PlacementNew()) T();
179 }
180 return field;
181 }
182
187 template <EnumType wantedType>
188 [[nodiscard]] typename TypeAt<EnumToType<wantedType>::index>::type* field()
189 {
190 if (wantedType == type)
191 {
192 return &fieldAt<EnumToType<wantedType>::index>();
193 }
194 return nullptr;
195 }
196
201 template <EnumType wantedType>
202 [[nodiscard]] const typename TypeAt<EnumToType<wantedType>::index>::type* field() const
203 {
204 if (wantedType == type)
205 {
206 return &fieldAt<EnumToType<wantedType>::index>();
207 }
208 return nullptr;
209 }
210
211 private:
212 struct Destruct
213 {
214 template <int Index>
215 void operator()(TaggedUnion& t1)
216 {
217 using T = typename TypeAt<Index>::type;
218 t1.fieldAt<Index>().~T();
219 }
220 };
221
222 struct Construct
223 {
224 template <int Index>
225 void operator()(TaggedUnion& t1)
226 {
227 using T = typename TypeAt<Index>::type;
228 new (&t1.fieldAt<Index>(), PlacementNew()) T();
229 }
230 };
231
232 struct CopyConstruct
233 {
234 template <int Index>
235 void operator()(TaggedUnion& t1, const TaggedUnion& t2)
236 {
237 using T = typename TypeAt<Index>::type;
238 new (&t1.fieldAt<Index>(), PlacementNew()) T(t2.fieldAt<Index>());
239 }
240 };
241
242 struct MoveConstruct
243 {
244 template <int Index>
245 void operator()(TaggedUnion& t1, TaggedUnion& t2)
246 {
247 using T = typename TypeAt<Index>::type;
248 new (&t1.fieldAt<Index>(), PlacementNew()) T(move(t2.fieldAt<Index>()));
249 }
250 };
251
252 struct CopyAssign
253 {
254 template <int Index>
255 void operator()(TaggedUnion& t1, const TaggedUnion& t2)
256 {
257 t1.fieldAt<Index>() = t2.fieldAt<Index>();
258 }
259 };
260
261 struct MoveAssign
262 {
263 template <int Index>
264 void operator()(TaggedUnion& t1, TaggedUnion& t2)
265 {
266 t1.fieldAt<Index>() = move(t2.fieldAt<Index>());
267 }
268 };
269
270 struct Equals
271 {
272 template <int Index>
273 static auto visit(TaggedUnion& t1, TaggedUnion& t2)
274 {
275 return t1.fieldAt<Index>() == move(t2.fieldAt<Index>());
276 }
277 };
278
279 template <typename Visitor, typename... Arguments>
280 auto visit(Visitor&& visitor, Arguments&&... args)
281 {
282 return RuntimeEnumVisit<Visitor>::visit(forward<Visitor>(visitor), type, *this, forward<Arguments>(args)...);
283 }
284
285 template <typename Visitor, size_t StartIndex = NumTypes>
286 struct RuntimeEnumVisit
287 {
288 static constexpr auto Index = StartIndex - 1;
289
290 template <typename... Args>
291 static auto visit(Visitor&& visitor, EnumType enumType, Args&... args)
292 {
293 if (enumType == TypeAt<Index>::value)
294 {
295 return visitor.template operator()<Index>(args...);
296 }
297 else
298 {
299 return RuntimeEnumVisit<Visitor, Index>::visit(forward<Visitor>(visitor), enumType, args...);
300 }
301 }
302 };
303
304 template <typename Visitor>
305 struct RuntimeEnumVisit<Visitor, 0>
306 {
307 template <typename... Args>
308 static auto visit(Visitor&& visitor, EnumType, Args&... args)
309 {
310 return visitor.template operator()<0>(args...);
311 }
312 };
313
314 template <int index>
315 [[nodiscard]] auto& fieldAt()
316 {
317 using T = typename TypeAt<index>::type;
318 return storage.template reinterpret_as<T>();
319 }
320
321 template <int index>
322 [[nodiscard]] const auto& fieldAt() const
323 {
324 using T = typename TypeAt<index>::type;
325 return storage.template reinterpret_as<const T>();
326 }
327
328 template <class ForwardIt>
329 static constexpr ForwardIt MaxElement(ForwardIt first, ForwardIt last)
330 {
331 if (first == last)
332 return last;
333 ForwardIt largest = first;
334 for (++first; first != last; ++first)
335 {
336 if (*largest < *first)
337 largest = first;
338 }
339
340 return largest;
341 }
342
343 template <class T>
344 static constexpr T MaxElement(std::initializer_list<T> list)
345 {
346 return *MaxElement(list.begin(), list.end());
347 }
348
349 template <class... Types>
350 struct ComputeMaxSizeAndAlignment;
351
352 template <class... Types>
353 struct ComputeMaxSizeAndAlignment<TypeTraits::TypeList<Types...>>
354 {
355 static constexpr size_t maxAlignment = MaxElement({alignof(typename Types::type)...});
356 static constexpr size_t maxSize = MaxElement({sizeof(typename Types::type)...});
357 };
358
359 using Storage = ComputeMaxSizeAndAlignment<typename Union::FieldsTypes>;
360
361 AlignedStorage<Storage::maxSize, Storage::maxAlignment> storage;
362
363 EnumType type;
364};
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
typename TypeListGet< T, N >::type TypeListGetT
Alias template to simplify accessing the retrieved type using TypeListGet.
Definition: TypeList.h:69
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition: Compiler.h:269
Associate a Type to an Enum, as required by TaggedUnion.
Definition: TaggedUnion.h:24
Extracts type T corresponding to enumeration wantedEnum at compile time.
Definition: TaggedUnion.h:110
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:131
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:128
const TypeAt< EnumToType< wantedType >::index >::type * field() const
Get a pointer to currently active field.
Definition: TaggedUnion.h:202
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:188
void assign(U &&other)
Assigns a compile time known enum type with an object U.
Definition: TaggedUnion.h:148
~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:170
TaggedUnion(TaggedUnion &&other)
Move constructor.
Definition: TaggedUnion.h:65