CVB++ 14.0
variant.hpp
1#pragma once
2
9#include <new>
10#include <exception>
11#include <utility>
12#include "_detail/variant_helper.hpp"
13
14namespace Cvb
15{
16
17CVB_BEGIN_INLINE_NS
18
19namespace Shims
20{
21
26{
27public:
29 bad_variant_access() = default;
30};
31
33static const constexpr size_t variant_npos = size_t(-1);
34
40template <size_t I, class T>
42
47template <class... TS>
48class variant final
49{
50 static_assert(Detail::NoneIs<std::is_void<TS>::value...>, "CVB: void types are not allowed");
51 static_assert(Detail::NoneIs<std::is_reference<TS>::value...>, "CVB: reference types are not allowed");
52 static_assert(Detail::NoneIs<std::is_const<TS>::value...>, "CVB: const types are not allowed");
53 static_assert(Detail::NoneIs<std::is_volatile<TS>::value...>, "CVB: volatile types are not allowed");
54
55# pragma region friend access
56 template <size_t I, class... ETS>
57 friend const typename variant_alternative<I, variant<ETS...>>::type & get(const variant<ETS...> &var);
58 template <size_t I, class... ETS>
59 friend typename variant_alternative<I, variant<ETS...>>::type & get(variant<ETS...> &var);
60 template <size_t I, class... ETS>
61 friend typename variant_alternative<I, variant<ETS...>>::type get(variant<ETS...> &&var);
62 template <class... ETS>
63 friend bool operator ==(const variant<ETS...> &lhs, const variant<ETS...> &rhs);
64 template <class... ETS>
65 friend bool operator <(const variant<ETS...> &lhs, const variant<ETS...> &rhs);
66# pragma endregion
67
68 static const constexpr size_t StorageSize = Detail::Max<sizeof(TS)...>;
69 static const constexpr size_t StorageAlignment = Detail::Max<alignof(TS)...>;
70
72
74 size_t index_;
76 Storage data_;
77
78 void delete_if_valid() noexcept;
79
80 template <class T>
81 void set(T &&value)
82 {
83 const constexpr auto exactIndex = Detail::IndexOf<T, TS...>; // exact type has precedence
84 const constexpr auto newIndex = exactIndex != variant_npos ? exactIndex : Detail::IndexOfAssignable<T, TS...>;
85 static_assert(newIndex != variant_npos, "CVB: assigned type not compatible to alternatives list");
86
87 using NewT = Detail::TypeAt<newIndex, TS...>;
88
89 delete_if_valid();
90 new(&data_) NewT(std::forward<T>(value));
91 index_ = newIndex;
92 }
93
94public:
97 //template <typename std::enable_if<std::is_default_constructible<Detail::TypeAt<0, TS...>>::value, int>::type = 0>
98 variant() noexcept
99 : index_(0)
100 {
101 new(&data_) Detail::TypeAt<0, TS...>{};
102 }
103
105 ~variant() noexcept { delete_if_valid(); }
106
112 variant(const variant &rhs);
113
119 variant(variant &&rhs);
120
127 variant &operator =(const variant &rhs);
128
135 variant &operator =(variant &&rhs);
136
143 template <class T, typename std::enable_if<!std::is_same<T, variant &>::value, int>::type = 0>
144 variant(T &&value)
145 : index_(variant_npos)
146 {
147 set(std::forward<T>(value));
148 }
149
161 template <class T, typename std::enable_if<!std::is_same<T, variant &>::value, int>::type = 0>
162 variant & operator =(T &&value)
163 {
164 set(std::forward<T>(value));
165
166 return *this;
167 }
168
176 template <size_t I, class... ARGS>
177 typename variant_alternative<I, variant<TS...>>::type & emplace(ARGS &&... args);
178
186 template <class T, class... ARGS>
187 T & emplace(ARGS &&... args)
188 {
189 const constexpr auto newIndex = Detail::IndexOf<T, TS...>;
190 static_assert(newIndex != variant_npos, "CVB: assigned type not in alternatives list");
191
192 return emplace<newIndex>(std::forward<ARGS>(args)...);
193 }
194
198 size_t index() const noexcept { return index_; }
199
205 void swap(variant &other);
206
212 bool valueless_by_exception() const noexcept { return index_ == variant_npos; }
213};
214
215#pragma region monostate
216
226struct monostate {};
227
230constexpr bool operator<(monostate, monostate) noexcept { return false; }
231
234constexpr bool operator>(monostate, monostate) noexcept { return false; }
235
238constexpr bool operator<=(monostate, monostate) noexcept { return true; }
239
242constexpr bool operator>=(monostate, monostate) noexcept { return true; }
243
246constexpr bool operator==(monostate, monostate) noexcept { return true; }
247
250constexpr bool operator!=(monostate, monostate) noexcept { return false; }
251
252#pragma endregion
253
254#pragma region variant_alternative
255
256template <size_t I, class... TS>
257struct variant_alternative<I, variant<TS...>>
258{
259 using type = Detail::TypeAt<I, TS...>;
260};
261
262template <size_t I, class T>
264
265template <size_t I, class T>
266struct variant_alternative<I, const T> : variant_alternative<I, typename std::remove_reference<T>::type> {};
267
268template <size_t I, class T>
269struct variant_alternative<I, volatile T> : variant_alternative<I, typename std::remove_reference<T>::type> {};
270
271template <size_t I, class T>
272struct variant_alternative<I, const volatile T> : variant_alternative<I, typename std::remove_reference<T>::type> {};
273
277template <size_t I, class T>
279
280#pragma endregion
281
282#pragma region variant_size
283
288template <class T>
290
291template <class... TS>
292struct variant_size<variant<TS...>> : Detail::SizeT<sizeof...(TS)> {};
293
294template <class T>
295struct variant_size<T &> : variant_size<T> {};
296
297template <class T>
298struct variant_size<const T> : variant_size<typename std::remove_reference<T>::type> {};
299
300template <class T>
301struct variant_size<volatile T> : variant_size<typename std::remove_reference<T>::type> {};
302
303template <class T>
304struct variant_size<const volatile T> : variant_size<typename std::remove_reference<T>::type> {};
305
310template <class T>
311static const constexpr size_t variant_alternatives_size_v = variant_size<T>::value;
312
313#pragma endregion
314
315#pragma region get<I>
316
327template <size_t I, class... TS>
328const variant_alternative_t<I, variant<TS...>> & get(const variant<TS...> &var)
329{
330 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
331 if(I != var.index())
332 throw bad_variant_access{};
333
334 return reinterpret_cast<const variant_alternative_t<I, variant<TS...>> &>(var.data_);
335}
336
338template <size_t I, class... TS>
340{
341 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
342 if(I != var.index())
343 throw bad_variant_access{};
344
345 return reinterpret_cast<variant_alternative_t<I, variant<TS...>> &>(var.data_);
346}
347
349template <size_t I, class... TS>
351{
352 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
353 if(I != var.index())
354 throw bad_variant_access{};
355
356 return reinterpret_cast<variant_alternative_t<I, variant<TS...>> &&>(var.data_);
357}
358
359#pragma endregion
360
361#pragma region get<T>
362
373template <class T, class... TS>
374const T & get(const variant<TS...> &var)
375{
376 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
377 static_assert(index != variant_npos, "CVB: type not in alternatives list");
378
379 return get<index>(var);
380}
381
383template <class T, class... TS>
385{
386 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
387 static_assert(index != variant_npos, "CVB: type not in alternatives list");
388
389 return get<index>(var);
390}
391
393template <class T, class... TS>
395{
396 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
397 static_assert(index != variant_npos, "CVB: type not in alternatives list");
398
399 return get<index>(std::move(var));
400}
401
402#pragma endregion
403
404#pragma region get_if<I>
405
416template <size_t I, class... TS>
417typename std::add_pointer<const variant_alternative_t<I, variant<TS...>>>::type get_if(const variant<TS...>* pvar) noexcept
418{
419 if(!pvar || pvar->index() != I)
420 return nullptr;
421
422 return &get<I>(*pvar);
423}
424
426template <size_t I, class... TS>
427typename std::add_pointer<variant_alternative_t<I, variant<TS...>>>::type get_if(variant<TS...>* pvar) noexcept
428{
429 if(!pvar || pvar->index() != I)
430 return nullptr;
431
432 return &get<I>(*pvar);
433}
434
435#pragma endregion
436
437#pragma region get_if<I>
438
449template <class T, class... TS>
451{
452 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
453 static_assert(index != variant_npos, "CVB: type not in alternatives list");
454
455 return get_if<index>(pvar);
456}
457
459template <class T, class... TS>
461{
462 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
463 static_assert(index != variant_npos, "CVB: type not in alternatives list");
464
465 return get_if<index>(pvar);
466}
467
468#pragma endregion
469
470#pragma region holds_alternative
471
480template <class T, class... TS>
481bool holds_alternative(const variant<TS...> &var) noexcept
482{
483 static_assert(Detail::Contains<T, TS...>, "CVB: alternative is not contained in variant.");
484 return var.index() == Detail::IndexOf<T, TS...>;
485}
486
487#pragma endregion
488
489#pragma region visit
490
491#pragma region VisitHelper
492
493namespace Detail
494{
495
497template <class T, size_t COUNT, size_t I = 0>
498struct VisitHelper1
499{
500 template <class VISITOR, class VARIANT>
501 T operator()(VISITOR &&visitor, VARIANT &&var) const
502 {
503 if(I == var.index())
504 return visitor(get<I>(std::forward<VARIANT>(var)));
505 else
506 return VisitHelper1<T, COUNT, I + 1>{}(std::forward<VISITOR>(visitor), std::forward<VARIANT>(var));
507 }
508};
509
510template <class T, size_t COUNT>
511struct VisitHelper1<T, COUNT, COUNT>
512{
513 template <class VISITOR, class VARIANT>
514 T operator()(VISITOR &&, VARIANT &&) const
515 {
516 throw bad_variant_access{};
517 }
518};
519
520} // namespace Detail
521
522#pragma endregion
523
535template <class VISITOR, class VARIANT>
536auto visit(VISITOR &&visitor, VARIANT &&var) -> decltype(visitor(get<0>(var)))
537{
538 using RESULT = decltype(visitor(get<0>(var)));
539 using HELPER = Detail::VisitHelper1<RESULT, variant_alternatives_size_v<VARIANT>>;
540
541 return HELPER{}(std::forward<VISITOR>(visitor), std::forward<VARIANT>(var));
542}
543
544#pragma endregion
545
546#pragma region comparison operators
547
558template <class... TS>
559bool operator ==(const variant<TS...> &lhs, const variant<TS...> &rhs)
560{
561 return lhs.index() == rhs.index()
562 && (lhs.valueless_by_exception() || visit(Detail::VariantEqual{&lhs.data_}, rhs));
563}
564
575template <class... TS>
576bool operator !=(const variant<TS...> &lhs, const variant<TS...> &rhs)
577{
578 return !(lhs == rhs);
579}
580
594template <class... TS>
595bool operator <(const variant<TS...> &lhs, const variant<TS...> &rhs)
596{
597 return !rhs.valueless_by_exception() &&
598 (
600 || lhs.index() < rhs.index()
601 || (lhs.index() == rhs.index() && visit(Detail::VariantLess{&lhs.data_}, rhs))
602 );
603}
604
613template <class... TS>
614bool operator <=(const variant<TS...> &lhs, const variant<TS...> &rhs)
615{
616 return !(rhs < lhs);
617}
618
619
628template <class... TS>
629bool operator >(const variant<TS...> &lhs, const variant<TS...> &rhs)
630{
631 return rhs < lhs;
632}
633
634
643template <class... TS>
644bool operator >=(const variant<TS...> &lhs, const variant<TS...> &rhs)
645{
646 return !(lhs < rhs);
647}
648
649#pragma endregion
650
651#pragma region variant copy
652
653template <class... TS>
655 : index_(rhs.index_)
656{
657 visit(Detail::VariantCopier{&data_}, rhs);
658}
659
660template <class... TS>
662{
663 if(this != &rhs)
664 {
665 delete_if_valid();
666 visit(Detail::VariantCopier{&data_}, rhs);
667 index_ = rhs.index_;
668 }
669
670 return *this;
671}
672
673#pragma endregion
674
675#pragma region variant move
676
677template <class... TS>
679 : index_(rhs.index_)
680{
681 visit(Detail::VariantMover{&data_}, std::move(rhs));
682 rhs.delete_if_valid();
683}
684
685template <class... TS>
687{
688 if(this != &rhs)
689 {
690 delete_if_valid();
691 visit(Detail::VariantMover{&data_}, std::move(rhs));
692 index_ = rhs.index_;
693 rhs.delete_if_valid();
694 }
695
696 return *this;
697}
698
699#pragma endregion
700
701#pragma region variant swap
702
703template <class... TS>
705{
706 // TODO: this can be made more efficient when we have the possibility to visit
707 // multiple variants.
708 auto old = std::move(*this);
709 *this = std::move(other);
710 other = std::move(old);
711}
712
713#pragma endregion
714
715#pragma region variant::delete_if_valid
716
718template <class... TS>
720{
721 if(!valueless_by_exception())
722 {
723 visit(Detail::VariantDestructor{}, *this);
724 index_ = variant_npos;
725 }
726}
727
728#pragma endregion
729
730#pragma region variant::emplace<I>
731
732template <class... TS>
733template <size_t I, class... ARGS>
734typename variant_alternative<I, variant<TS...>>::type & variant<TS...>::emplace(ARGS &&... args)
735{
736 using NewT = Detail::TypeAt<I, TS...>;
737
738 delete_if_valid();
739 auto result = new(&data_) NewT(std::forward<ARGS>(args)...);
740 index_ = I;
741
742 return *result;
743}
744
745#pragma endregion
746
747} // namespace Shims
748
749CVB_END_INLINE_NS
750
751} // namespace Cvb
752
753namespace std
754{
756 template <class... TS>
758 {
759 lhs.swap(rhs);
760 }
761}
Error when accessing not set alternative.
Definition: variant.hpp:26
bad_variant_access()=default
Default ctor.
This class is a replacement for C++17 std::variant.
Definition: variant.hpp:49
variant & operator=(const variant &rhs)
Copy operator.
Definition: variant.hpp:661
size_t index() const noexcept
Gets the zero-based index of the alternative held by this instance.
Definition: variant.hpp:198
bool valueless_by_exception() const noexcept
Returns false only if this instance holds a value.
Definition: variant.hpp:212
variant() noexcept
Default ctor creating a variant with default value of the first alternative type.
Definition: variant.hpp:98
variant(T &&value)
Ctor from an object of supported type T.
Definition: variant.hpp:144
std::add_pointer< constvariant_alternative_t< I, variant< TS... > > >::type get_if(const variant< TS... > *pvar) noexcept
Tries to get the value of the given variant pointer pvar as the alternative with index I.
Definition: variant.hpp:417
variant_alternative< I, variant< TS... > >::type & emplace(ARGS &&... args)
Emplaces an object as alternative at index I.
Definition: variant.hpp:734
std::add_pointer< constT >::type get_if(const variant< TS... > *pvar) noexcept
Tries to get the value of the given variant pointer pvar as the alternative with type T.
Definition: variant.hpp:450
auto visit(VISITOR &&visitor, VARIANT &&var) -> decltype(visitor(get< 0 >(var)))
Visits the given variant var. Cvb::Shims::visit can only visit one variant (not multiple like std::vi...
Definition: variant.hpp:536
~variant() noexcept
Destructor.
Definition: variant.hpp:105
void swap(variant &other)
Swaps the content of this variant with the other one.
Definition: variant.hpp:704
const variant_alternative_t< I, variant< TS... > > & get(const variant< TS... > &var)
Gets the value of the given variant var as the alternative with index I.
Definition: variant.hpp:328
bool holds_alternative(const variant< TS... > &var) noexcept
Gets whether the variant var holds an instance of type T.
Definition: variant.hpp:481
const T & get(const variant< TS... > &var)
Gets the value of the given variant var as the alternative with type T.
Definition: variant.hpp:374
T & emplace(ARGS &&... args)
Emplaces an object as a type T.
Definition: variant.hpp:187
static const constexpr size_t variant_npos
Returned by variant::index() on invalid state.
Definition: variant.hpp:33
typename variant_alternative< I, T >::type variant_alternative_t
Helper type alias for Shims::variant_alternative.
Definition: variant.hpp:278
std::add_pointer< variant_alternative_t< I, variant< TS... > > >::type get_if(variant< TS... > *pvar) noexcept
Definition: variant.hpp:427
variant_alternative_t< I, variant< TS... > > & get(variant< TS... > &var)
Definition: variant.hpp:339
Root namespace for the Image Manager interface.
Definition: c_barcode.h:24
Unit type intended for use as a well-behaved empty alternative in variant.
Definition: variant.hpp:226
constexpr bool operator>=(monostate, monostate) noexcept
Definition: variant.hpp:242
constexpr bool operator!=(monostate, monostate) noexcept
Definition: variant.hpp:250
constexpr bool operator<(monostate, monostate) noexcept
Definition: variant.hpp:230
constexpr bool operator==(monostate, monostate) noexcept
Definition: variant.hpp:246
constexpr bool operator<=(monostate, monostate) noexcept
Definition: variant.hpp:238
constexpr bool operator>(monostate, monostate) noexcept
Definition: variant.hpp:234
Compile-time index access to the type of the alternatives at index I.
Definition: variant.hpp:41
Get the number of alternatives in a variant.
Definition: variant.hpp:289