aws-crt-cpp
C++ wrapper around the aws-c-* libraries. Provides Cross-Platform Transport Protocols and SSL/TLS implementations for C++.
Loading...
Searching...
No Matches
Variant.h
Go to the documentation of this file.
1#pragma once
7#include <aws/common/assert.h>
8#include <aws/crt/Utility.h>
9
10#include <algorithm>
11#include <type_traits>
12#include <utility>
13
14namespace Aws
15{
16 namespace Crt
17 {
18 namespace VariantDetail
19 {
20 template <typename T> constexpr const T &ConstExprMax(const T &a, const T &b)
21 {
22 return (a < b) ? b : a;
23 }
24
25 namespace ParameterPackSize
26 {
27 // Returns a max of sizeof(T) over all T in a template parameter pack
28 template <typename Last> constexpr std::size_t GetMaxSizeOf(std::size_t curMax = 0)
29 {
30 return ConstExprMax(curMax, sizeof(Last));
31 }
32
33 template <typename First, typename Second, typename... Rest>
34 constexpr std::size_t GetMaxSizeOf(std::size_t curMax = 0)
35 {
37 }
38
39 // some old gcc versions does not work with alignas(Ts..)
40 template <typename Last> constexpr std::size_t AlignAsPack(std::size_t curMax = 0)
41 {
42 return ConstExprMax(curMax, alignof(Last));
43 }
44
45 template <typename First, typename Second, typename... Rest>
46 constexpr std::size_t AlignAsPack(std::size_t curMax = 0)
47 {
49 }
50 } // namespace ParameterPackSize
51
52 namespace Index
53 {
55
56 template <typename T, typename Last> constexpr VariantIndex GetIndexOf(VariantIndex curIndex = 0)
57 {
58 return std::is_same<T, Last>::value ? curIndex : -1;
59 }
60
61 template <typename T, typename First, typename Second, typename... Rest>
63 {
64 return std::is_same<T, First>::value ? curIndex : GetIndexOf<T, Second, Rest...>(++curIndex);
65 }
66 } // namespace Index
67
68 namespace Checker
69 {
70 // Returns True if the template parameter pack Ts has a type T, i.e. ContainsType<T, Ts>() == true if T
71 // is in the list of Ts
72 template <typename T, typename Last> constexpr bool ContainsType()
73 {
74 return std::is_same<T, Last>::value;
75 }
76
77 template <typename T, typename First, typename Second, typename... Rest> constexpr bool ContainsType()
78 {
79 return std::is_same<T, First>::value || ContainsType<T, Second, Rest...>();
80 }
81
82 // a case when the template parameter pack is empty (i.e. Variant<>)
83 template <typename T> constexpr bool ContainsType()
84 {
85 return false;
86 }
87
88 template <typename T, typename... Ts> struct HasType
89 {
90 static const bool value = ContainsType<T, Ts...>();
91 };
92 } // namespace Checker
93#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG)
94 namespace VariantDebug
95 {
96 template <typename... Ts> class VariantDebugBrowser
97 {
98 public:
100 std::tuple<typename std::add_pointer<Ts>::type...> as_tuple;
101
102 private:
103 template <IndexT Index, typename First, typename Second, typename... Rest>
104 void InitTuple(char *storage)
105 {
106 First *value = reinterpret_cast<First *>(storage);
107 std::get<Index>(as_tuple) = value;
108 InitTuple<Index + 1, Second, Rest...>(storage);
109 }
110
111 template <IndexT Index, typename Last> void InitTuple(char *storage)
112 {
113 Last *value = reinterpret_cast<Last *>(storage);
114 std::get<Index>(as_tuple) = value;
115 }
116 };
117 } // namespace VariantDebug
118#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */
119 } // namespace VariantDetail
120
121 template <std::size_t Index, typename... Ts> class VariantAlternative;
122
127 template <typename... Ts> class Variant
128 {
129 private:
130 template <std::size_t Index> using ThisVariantAlternative = VariantAlternative<Index, Ts...>;
131
132 template <typename OtherT>
133 using EnableIfOtherIsThisVariantAlternative = typename std::
134 enable_if<VariantDetail::Checker::HasType<typename std::decay<OtherT>::type, Ts...>::value, int>::type;
135
136 public:
138 static constexpr std::size_t AlternativeCount = sizeof...(Ts);
139
141 {
142 using FirstAlternative = typename ThisVariantAlternative<0>::type;
143 new (m_storage) FirstAlternative();
144 m_index = 0;
145 }
146
147 Variant(const Variant &other)
148 {
149 AWS_FATAL_ASSERT(other.m_index != -1);
150 m_index = other.m_index;
151 VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor());
152 }
153
155 {
156 AWS_FATAL_ASSERT(other.m_index != -1);
157 m_index = other.m_index;
158 VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor());
159 }
160
161 template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> Variant(const T &val)
162 {
163 static_assert(
165 "This variant does not have such alternative T.");
166 static_assert(
167 sizeof(T) <= STORAGE_SIZE,
168 "Attempting to instantiate a Variant with a type bigger than all alternatives.");
169
170 using PlainT = typename std::decay<T>::type;
171 new (m_storage) PlainT(val);
173 AWS_ASSERT(m_index != -1);
174 }
175
176 template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> Variant(T &&val)
177 {
178 static_assert(
180 "This variant does not have such alternative T.");
181 static_assert(
182 sizeof(T) <= STORAGE_SIZE,
183 "Attempting to instantiate a Variant with a type bigger than all alternatives.");
184
185 using PlainT = typename std::decay<T>::type;
186 new (m_storage) PlainT(std::forward<T>(val));
188 AWS_ASSERT(m_index != -1);
189 }
190
191 // An overload to initialize with an Alternative T in-place
192 template <typename T, typename... Args> explicit Variant(Aws::Crt::InPlaceTypeT<T>, Args &&...args)
193 {
194 static_assert(
196 "This variant does not have such alternative T.");
197 static_assert(
198 sizeof(T) <= STORAGE_SIZE,
199 "Attempting to instantiate a Variant with a type bigger than all alternatives.");
200
201 using PlainT = typename std::decay<T>::type;
202 new (m_storage) PlainT(std::forward<Args>(args)...);
204 AWS_ASSERT(m_index != -1);
205 }
206
207 Variant &operator=(const Variant &other)
208 {
209 if (this != &other)
210 {
211 AWS_FATAL_ASSERT(other.m_index != -1);
212 if (m_index != other.m_index)
213 {
214 Destroy();
215 m_index = other.m_index;
216 VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor());
217 }
218 else
219 {
220 VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner());
221 }
222 }
223 return *this;
224 }
225
227 {
228 if (this != &other)
229 {
230 AWS_FATAL_ASSERT(other.m_index != -1);
231 if (m_index != other.m_index)
232 {
233 Destroy();
234 m_index = other.m_index;
235 VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor());
236 }
237 else
238 {
239 VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner());
240 };
241 }
242 return *this;
243 }
244
245 /* emplace */
246 template <typename T, typename... Args, EnableIfOtherIsThisVariantAlternative<T> = 1>
248 {
249 static_assert(
251 "This variant does not have such alternative T.");
252 static_assert(
253 sizeof(T) <= STORAGE_SIZE,
254 "Attempting to instantiate a Variant with a type bigger than all alternatives.");
255
256 Destroy();
257
258 using PlainT = typename std::decay<T>::type;
259 new (m_storage) PlainT(std::forward<Args>(args)...);
261 AWS_ASSERT(m_index != -1);
262
263 T *value = reinterpret_cast<T *>(m_storage);
264 return *value;
265 }
266
267 template <std::size_t Index, typename... Args>
268 auto emplace(Args &&...args) -> typename ThisVariantAlternative<Index>::type &
269 {
270 static_assert(Index < AlternativeCount, "Unknown alternative index to emplace");
271 using AlternativeT = typename ThisVariantAlternative<Index>::type;
272
273 return emplace<AlternativeT, Args...>(std::forward<Args>(args)...);
274 }
275
276 template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> bool holds_alternative() const
277 {
278 AWS_ASSERT(m_index != -1);
279 return m_index == VariantDetail::Index::GetIndexOf<T, Ts...>();
280 }
281
282 /* non-const get */
283 template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> T &get()
284 {
286 T *value = reinterpret_cast<T *>(m_storage);
287 return *value;
288 }
289
290 template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> T *get_if()
291 {
293 {
294 T *value = reinterpret_cast<T *>(m_storage);
295 return value;
296 }
297 else
298 {
299 return nullptr;
300 }
301 }
302
303 template <std::size_t Index> auto get() -> typename ThisVariantAlternative<Index>::type &
304 {
305 static_assert(Index < AlternativeCount, "Unknown alternative index to get");
307 using AlternativeT = typename ThisVariantAlternative<Index>::type;
308 AlternativeT *ret = reinterpret_cast<AlternativeT *>(m_storage);
309 return *ret;
310 }
311
312 /* const get */
313 template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> const T &get() const
314 {
316 const T *value = reinterpret_cast<const T *>(m_storage);
317 return *value;
318 }
319
320 template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> const T *get_if() const
321 {
323 {
324 T *value = reinterpret_cast<T *>(m_storage);
325 return value;
326 }
327 else
328 {
329 return nullptr;
330 }
331 }
332
333 template <std::size_t Index> auto get() const -> const typename ThisVariantAlternative<Index>::type &
334 {
335 static_assert(Index < AlternativeCount, "Unknown alternative index to get");
336 AWS_ASSERT(Index == m_index);
337 using AlternativeT = typename ThisVariantAlternative<Index>::type;
338 const AlternativeT *ret = reinterpret_cast<const AlternativeT *>(m_storage);
339 return *ret;
340 }
341
342 /* This is just a templated way to say
343 * "int*" for
344 * a VariantAlternative<0, Variant<int, char, long>()>*/
345 template <std::size_t Index>
347 typename std::add_pointer<typename ThisVariantAlternative<Index>::type>::type;
348
349 template <std::size_t Index> auto get_if() -> RawAlternativePointerT<Index>
350 {
351 static_assert(Index < AlternativeCount, "Unknown alternative index to get");
353 {
355 AlternativePtrT value = reinterpret_cast<AlternativePtrT>(m_storage);
356 return value;
357 }
358 else
359 {
360 return nullptr;
361 }
362 }
363
364 template <std::size_t Index>
365 using ConstRawAlternativePointerT = typename std::add_pointer<
366 typename std::add_const<typename ThisVariantAlternative<Index>::type>::type>::type;
367
368 template <std::size_t Index> auto get_if() const -> ConstRawAlternativePointerT<Index>
369 {
370 static_assert(Index < AlternativeCount, "Unknown alternative index to get");
372 {
374 AlternativePtrT value = reinterpret_cast<AlternativePtrT>(m_storage);
375 return value;
376 }
377 else
378 {
379 return nullptr;
380 }
381 }
382
383 std::size_t index() const { return m_index; }
384
385 ~Variant() { Destroy(); }
386
387 template <typename VisitorT> void Visit(VisitorT &&visitor)
388 {
389 return VisitorUtil<0, Ts...>::Visit(this, std::forward<VisitorT>(visitor));
390 }
391
392 private:
393 static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf<Ts...>();
394
395 alignas(VariantDetail::ParameterPackSize::AlignAsPack<Ts...>()) char m_storage[STORAGE_SIZE];
396 IndexT m_index = -1;
397#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG)
398 VariantDetail::VariantDebug::VariantDebugBrowser<Ts...> browser = m_storage;
399#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */
400
401 template <size_t Index> constexpr bool holds_alternative() const { return Index == m_index; }
402
403 struct Destroyer
404 {
405 template <typename AlternativeT> void operator()(AlternativeT &&value) const
406 {
407 (void)value;
408 using PlaintT = typename std::remove_reference<AlternativeT>::type;
409 value.~PlaintT();
410 }
411 };
412
413 void Destroy()
414 {
415 AWS_FATAL_ASSERT(m_index != -1);
416 Visit(Destroyer());
417
418 m_index = -1;
419 }
420
421 struct CopyMoveConstructor
422 {
423 template <typename AlternativeT> void operator()(AlternativeT &&value, AlternativeT &&other) const
424 {
425 using PlaintT = typename std::remove_reference<AlternativeT>::type;
426 new (&value) PlaintT(std::move<AlternativeT>(other));
427 }
428
429 template <typename AlternativeT, typename ConstAlternativeT>
430 void operator()(AlternativeT &&value, ConstAlternativeT &other) const
431 {
432 using PlaintT = typename std::remove_reference<AlternativeT>::type;
433 using PlaintOtherT =
434 typename std::remove_const<typename std::remove_reference<AlternativeT>::type>::type;
435 static_assert(std::is_same<PlaintT, PlaintOtherT>::value, "Incompatible types");
436
437 new (&value) PlaintT(other);
438 }
439 };
440
441 struct CopyMoveAssigner
442 {
443 template <typename AlternativeT> void operator()(AlternativeT &&value, AlternativeT &&other) const
444 {
445 value = std::move(other);
446 }
447
448 template <typename AlternativeT, typename ConstAlternativeT>
449 void operator()(AlternativeT &&value, ConstAlternativeT &other) const
450 {
451 using PlaintT = typename std::remove_reference<AlternativeT>::type;
452 using PlaintOtherT =
453 typename std::remove_const<typename std::remove_reference<AlternativeT>::type>::type;
454 static_assert(std::is_same<PlaintT, PlaintOtherT>::value, "Incompatible types");
455
456 value = other;
457 }
458 };
459
460 template <IndexT Index, typename... Args> struct VisitorUtil;
461
462 template <IndexT Index, typename First, typename Second, typename... Rest>
463 struct VisitorUtil<Index, First, Second, Rest...>
464 {
465 template <typename VisitorStruct> static void Visit(Variant *pThis, VisitorStruct &&visitor)
466 {
467 static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
468
469 if (Index == pThis->m_index)
470 {
472 AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
473 visitor(*value);
474 }
475 else
476 {
478 pThis, std::forward<VisitorStruct>(visitor));
479 }
480 }
481
482 template <typename VisitorStruct>
483 static void VisitBinary(Variant<Ts...> *pThis, Variant<Ts...> &&other, VisitorStruct &&visitor)
484 {
485 static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
486
487 if (Index == pThis->m_index)
488 {
490 AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
491 visitor(*value, other.get<AlternativeT>());
492 }
493 else
494 {
495 VisitorUtil<static_cast<IndexT>(Index + 1), Second, Rest...>::VisitBinary(
496 pThis, std::forward<Variant<Ts...>>(other), std::forward<VisitorStruct>(visitor));
497 }
498 }
499
500 template <typename VisitorStruct>
501 static void VisitBinary(Variant<Ts...> *pThis, const Variant<Ts...> &other, VisitorStruct &&visitor)
502 {
503 static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
504
505 if (Index == pThis->m_index)
506 {
508 AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
509 const AlternativeT &otherValue = other.get<AlternativeT>();
510 visitor(*value, otherValue);
511 }
512 else
513 {
514 VisitorUtil<static_cast<IndexT>(Index + 1), Second, Rest...>::VisitBinary(
515 pThis, other, std::forward<VisitorStruct>(visitor));
516 }
517 }
518 };
519
520 template <IndexT Index, typename Last> struct VisitorUtil<Index, Last>
521 {
522 template <typename VisitorStruct> static void Visit(Variant *pThis, VisitorStruct &&visitor)
523 {
524 static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
525
526 if (Index == pThis->m_index)
527 {
529 AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
530 visitor(*value);
531 }
532 else
533 {
534 AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!");
535 }
536 }
537
538 template <typename VisitorStruct>
539 static void VisitBinary(Variant<Ts...> *pThis, Variant<Ts...> &&other, VisitorStruct &&visitor)
540 {
541 static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
542
543 if (Index == pThis->m_index)
544 {
546 AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
547 visitor(*value, other.get<AlternativeT>());
548 }
549 else
550 {
551 AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!");
552 }
553 }
554
555 template <typename VisitorStruct>
556 static void VisitBinary(Variant<Ts...> *pThis, const Variant<Ts...> &other, VisitorStruct &&visitor)
557 {
558 static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
559
560 if (Index == pThis->m_index)
561 {
563 AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
564 const AlternativeT &otherValue = other.get<AlternativeT>();
565 visitor(*value, otherValue);
566 }
567 else
568 {
569 AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!");
570 }
571 }
572 };
573 };
574
575 /* Helper template to get an actual type from an Index */
576 template <std::size_t Index, typename... Ts> class VariantAlternative
577 {
578 public:
579 // uses std::tuple as a helper struct to provide index-based access of a parameter pack
580 using type = typename std::tuple_element<Index, std::tuple<Ts...>>::type;
581
583
585 };
586
587 template <typename T> class VariantSize
588 {
589 constexpr static const std::size_t Value = T::AlternativeCount;
590 };
591 } // namespace Crt
592} // namespace Aws
Definition Variant.h:577
VariantAlternative(const Variant< Ts... > &)
Definition Variant.h:582
typename std::tuple_element< Index, std::tuple< Ts... > >::type type
Definition Variant.h:580
VariantAlternative(const Variant< Ts... > *)
Definition Variant.h:584
Definition Variant.h:128
Variant & operator=(Variant &&other)
Definition Variant.h:226
Variant(Aws::Crt::InPlaceTypeT< T >, Args &&...args)
Definition Variant.h:192
~Variant()
Definition Variant.h:385
void Visit(VisitorT &&visitor)
Definition Variant.h:387
auto get() const -> const typename ThisVariantAlternative< Index >::type &
Definition Variant.h:333
auto emplace(Args &&...args) -> typename ThisVariantAlternative< Index >::type &
Definition Variant.h:268
auto get_if() const -> ConstRawAlternativePointerT< Index >
Definition Variant.h:368
T * get_if()
Definition Variant.h:290
const T & get() const
Definition Variant.h:313
const T * get_if() const
Definition Variant.h:320
auto get() -> typename ThisVariantAlternative< Index >::type &
Definition Variant.h:303
typename std::add_pointer< typename ThisVariantAlternative< Index >::type >::type RawAlternativePointerT
Definition Variant.h:347
Variant(Variant &&other)
Definition Variant.h:154
Variant(const Variant &other)
Definition Variant.h:147
Variant(const T &val)
Definition Variant.h:161
T & emplace(Args &&...args)
Definition Variant.h:247
T & get()
Definition Variant.h:283
std::size_t index() const
Definition Variant.h:383
auto get_if() -> RawAlternativePointerT< Index >
Definition Variant.h:349
static constexpr std::size_t AlternativeCount
Definition Variant.h:138
typename std::add_pointer< typename std::add_const< typename ThisVariantAlternative< Index >::type >::type >::type ConstRawAlternativePointerT
Definition Variant.h:366
Variant(T &&val)
Definition Variant.h:176
bool holds_alternative() const
Definition Variant.h:276
Variant()
Definition Variant.h:140
Variant & operator=(const Variant &other)
Definition Variant.h:207
VariantDetail::Index::VariantIndex IndexT
Definition Variant.h:137
Definition Variant.h:588
constexpr bool ContainsType()
Definition Variant.h:72
constexpr VariantIndex GetIndexOf(VariantIndex curIndex=0)
Definition Variant.h:56
short VariantIndex
Definition Variant.h:54
constexpr std::size_t AlignAsPack(std::size_t curMax=0)
Definition Variant.h:40
constexpr std::size_t GetMaxSizeOf(std::size_t curMax=0)
Definition Variant.h:28
constexpr const T & ConstExprMax(const T &a, const T &b)
Definition Variant.h:20
std::unique_ptr< T, std::function< void(T *)> > ScopedResource
Definition Types.h:163
Definition Allocator.h:11
static const bool value
Definition Variant.h:90