CVB++ 15.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) noexcept;
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) // NOLINT
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
333template <size_t I, class... TS>
334const variant_alternative_t<I, variant<TS...>> & get(const variant<TS...> &var)
335{
336 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
337 if(I != var.index())
338 throw bad_variant_access{};
339
340 return reinterpret_cast<const variant_alternative_t<I, variant<TS...>> &>(var.data_);
341}
342
347template <size_t I, class... TS>
349{
350 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
351 if(I != var.index())
352 throw bad_variant_access{};
353
354 return reinterpret_cast<variant_alternative_t<I, variant<TS...>> &>(var.data_);
355}
356
361template <size_t I, class... TS>
363{
364 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
365 if(I != var.index())
366 throw bad_variant_access{};
367
368 return reinterpret_cast<variant_alternative_t<I, variant<TS...>> &&>(var.data_);
369}
370
371#pragma endregion
372
373#pragma region get<T>
374
391template <class T, class... TS>
392const T & get(const variant<TS...> &var)
393{
394 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
395 static_assert(index != variant_npos, "CVB: type not in alternatives list");
396
397 return get<index>(var);
398}
399
404template <class T, class... TS>
406{
407 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
408 static_assert(index != variant_npos, "CVB: type not in alternatives list");
409
410 return get<index>(var);
411}
412
417template <class T, class... TS>
419{
420 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
421 static_assert(index != variant_npos, "CVB: type not in alternatives list");
422
423 return get<index>(std::move(var));
424}
425
426#pragma endregion
427
428#pragma region get_if<I>
429
441template <size_t I, class... TS>
442typename std::add_pointer<const variant_alternative_t<I, variant<TS...>>>::type get_if(const variant<TS...>* pvar) noexcept
443{
444 if(!pvar || pvar->index() != I)
445 return nullptr;
446
447 return &get<I>(*pvar);
448}
449
454template <size_t I, class... TS>
455typename std::add_pointer<variant_alternative_t<I, variant<TS...>>>::type get_if(variant<TS...>* pvar) noexcept
456{
457 if(!pvar || pvar->index() != I)
458 return nullptr;
459
460 return &get<I>(*pvar);
461}
462
463#pragma endregion
464
465#pragma region get_if<I>
466
483template <class T, class... TS>
485{
486 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
487 static_assert(index != variant_npos, "CVB: type not in alternatives list");
488
489 return get_if<index>(pvar);
490}
491
496template <class T, class... TS>
498{
499 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
500 static_assert(index != variant_npos, "CVB: type not in alternatives list");
501
502 return get_if<index>(pvar);
503}
504
505#pragma endregion
506
507#pragma region holds_alternative
508
517template <class T, class... TS>
518bool holds_alternative(const variant<TS...> &var) noexcept
519{
520 static_assert(Detail::Contains<T, TS...>, "CVB: alternative is not contained in variant.");
521 return var.index() == Detail::IndexOf<T, TS...>;
522}
523
524#pragma endregion
525
526#pragma region visit
527
528#pragma region VisitHelper
529
530namespace Detail
531{
532
534template <class T, size_t COUNT, size_t I = 0>
535struct VisitHelper1
536{
537 template <class VISITOR, class VARIANT>
538 T operator()(VISITOR &&visitor, VARIANT &&var) const
539 {
540 if(I == var.index())
541 return visitor(get<I>(std::forward<VARIANT>(var)));
542 else
543 return VisitHelper1<T, COUNT, I + 1>{}(std::forward<VISITOR>(visitor), std::forward<VARIANT>(var));
544 }
545};
546
547template <class T, size_t COUNT>
548struct VisitHelper1<T, COUNT, COUNT>
549{
550 template <class VISITOR, class VARIANT>
551 T operator()(VISITOR &&, VARIANT &&) const
552 {
553 throw bad_variant_access{};
554 }
555};
556
557} // namespace Detail
558
559#pragma endregion
560
572template <class VISITOR, class VARIANT>
573auto visit(VISITOR &&visitor, VARIANT &&var) -> decltype(visitor(get<0>(var)))
574{
575 using RESULT = decltype(visitor(get<0>(var)));
576 using HELPER = Detail::VisitHelper1<RESULT, variant_alternatives_size_v<VARIANT>>;
577
578 return HELPER{}(std::forward<VISITOR>(visitor), std::forward<VARIANT>(var));
579}
580
581#pragma endregion
582
583#pragma region comparison operators
584
595template <class... TS>
596bool operator ==(const variant<TS...> &lhs, const variant<TS...> &rhs)
597{
598 return lhs.index() == rhs.index()
599 && (lhs.valueless_by_exception() || visit(Detail::VariantEqual{&lhs.data_}, rhs));
600}
601
612template <class... TS>
613bool operator !=(const variant<TS...> &lhs, const variant<TS...> &rhs)
614{
615 return !(lhs == rhs);
616}
617
631template <class... TS>
632bool operator <(const variant<TS...> &lhs, const variant<TS...> &rhs)
633{
634 return !rhs.valueless_by_exception() &&
635 (
637 || lhs.index() < rhs.index()
638 || (lhs.index() == rhs.index() && visit(Detail::VariantLess{&lhs.data_}, rhs))
639 );
640}
641
650template <class... TS>
651bool operator <=(const variant<TS...> &lhs, const variant<TS...> &rhs)
652{
653 return !(rhs < lhs);
654}
655
656
665template <class... TS>
666bool operator >(const variant<TS...> &lhs, const variant<TS...> &rhs)
667{
668 return rhs < lhs;
669}
670
671
680template <class... TS>
681bool operator >=(const variant<TS...> &lhs, const variant<TS...> &rhs)
682{
683 return !(lhs < rhs);
684}
685
686#pragma endregion
687
688#pragma region variant copy
689
690template <class... TS>
692 : index_(rhs.index_)
693{
694 visit(Detail::VariantCopier{&data_}, rhs);
695}
696
697template <class... TS>
699{
700 if(this != &rhs)
701 {
702 delete_if_valid();
703 visit(Detail::VariantCopier{&data_}, rhs);
704 index_ = rhs.index_;
705 }
706
707 return *this;
708}
709
710#pragma endregion
711
712#pragma region variant move
713
714template <class... TS>
716 : index_(rhs.index_)
717{
718 try
719 {
720 visit(Detail::VariantMover{ &data_ }, std::move(rhs));
721 rhs.delete_if_valid(); //NOLINT(bugprone-use-after-move)
722 }
723 catch (...)
724 {
725 // will never throw, but visit is capable of thowing so we must satisfy the compiler.
726 }
727}
728
729template <class... TS>
731{
732 if(this != &rhs)
733 {
734 delete_if_valid();
735 visit(Detail::VariantMover{&data_}, std::move(rhs));
736 index_ = rhs.index_;
737 rhs.delete_if_valid();
738 }
739
740 return *this;
741}
742
743#pragma endregion
744
745#pragma region variant swap
746
747template <class... TS>
749{
750 // TODO: this can be made more efficient when we have the possibility to visit
751 // multiple variants.
752 auto old = std::move(*this);
753 *this = std::move(other);
754 other = std::move(old);
755}
756
757#pragma endregion
758
759#pragma region variant::delete_if_valid
760
762template <class... TS>
764{
765 try
766 {
767 if (!valueless_by_exception())
768 {
769 visit(Detail::VariantDestructor{}, *this);
770 index_ = variant_npos;
771 }
772 }
773 catch (...)
774 {
775 // will never throw, but visit is capable of thowing so we must satisfy the compiler.
776 }
777}
778
779#pragma endregion
780
781#pragma region variant::emplace<I>
782
783template <class... TS>
784template <size_t I, class... ARGS>
785typename variant_alternative<I, variant<TS...>>::type & variant<TS...>::emplace(ARGS &&... args)
786{
787 using NewT = Detail::TypeAt<I, TS...>;
788
789 delete_if_valid();
790 auto result = new(&data_) NewT(std::forward<ARGS>(args)...); // NOLINT(cppcoreguidelines-owning-memory)
791 index_ = I;
792
793 return *result;
794}
795
796#pragma endregion
797
798} // namespace Shims
799
800CVB_END_INLINE_NS
801
802} // namespace Cvb
803
804namespace std // NOLINT(cert-dcl58-cpp)
805{
807 template <class... TS>
808 void swap(Cvb::Shims::variant<TS...> &lhs, Cvb::Shims::variant<TS...> &rhs) // NOLINT(cert-dcl58-cpp)
809 {
810 lhs.swap(rhs);
811 }
812}
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:698
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
variant_alternative< I, variant< TS... > >::type & emplace(ARGS &&... args)
Emplaces an object as alternative at index I.
Definition: variant.hpp:785
std::add_pointer< constT >::type get_if(const variant< TS... > *pvar) noexcept
Definition: variant.hpp:484
auto visit(VISITOR &&visitor, VARIANT &&var) -> decltype(visitor(get< 0 >(var)))
Visits the given Shims::variant var. Cvb::Shims::visit can only visit one variant (not multiple like ...
Definition: variant.hpp:573
~variant() noexcept
Destructor.
Definition: variant.hpp:105
void swap(variant &other)
Swaps the content of this variant with the other one.
Definition: variant.hpp:748
std::add_pointer< T >::type get_if(variant< TS... > *pvar) noexcept
Definition: variant.hpp:497
const variant_alternative_t< I, variant< TS... > > & get(const variant< TS... > &var)
Definition: variant.hpp:334
std::add_pointer< variant_alternative_t< I, variant< TS... > > >::type get_if(variant< TS... > *pvar) noexcept
Definition: variant.hpp:455
T get(variant< TS... > &&var)
Definition: variant.hpp:418
variant_alternative_t< I, variant< TS... > > get(variant< TS... > &&var)
Definition: variant.hpp:362
variant_alternative_t< I, variant< TS... > > & get(variant< TS... > &var)
Definition: variant.hpp:348
T & get(variant< TS... > &var)
Definition: variant.hpp:405
bool holds_alternative(const variant< TS... > &var) noexcept
Gets whether the Shims::variant var holds an instance of type T.
Definition: variant.hpp:518
const T & get(const variant< TS... > &var)
Definition: variant.hpp:392
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
Root namespace for the Image Manager interface.
Definition: c_barcode.h:15
Unit type intended for use as a well-behaved empty alternative in Shims::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