CVB++ 15.0
variant.hpp
1#pragma once
2
8
9#include <new>
10#include <exception>
11#include <utility>
12#include "_detail/variant_helper.hpp"
13
14namespace Cvb
15{
16
17 CVB_BEGIN_INLINE_NS
18
19 namespace Shims
20 {
21
26 {
27 public:
29 bad_variant_access() = default;
30 };
31
33 static const constexpr size_t variant_npos = size_t(-1);
34
40 template <size_t I, class T>
42
47 template <class... TS>
48 class 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
94 public:
97 // template <typename std::enable_if<std::is_default_constructible<Detail::TypeAt<0, TS...>>::value, int>::type =
98 // 0>
99 variant() noexcept
100 : index_(0)
101 {
102 new (&data_) Detail::TypeAt<0, TS...>{};
103 }
104
106 ~variant() noexcept
107 {
108 delete_if_valid();
109 }
110
116 variant(const variant &rhs);
117
123 variant(variant &&rhs) noexcept;
124
131 variant &operator=(const variant &rhs);
132
139 variant &operator=(variant &&rhs);
140
147 template <class T, typename std::enable_if<!std::is_same<T, variant &>::value, int>::type = 0>
148 variant(T &&value) // NOLINT
149 : index_(variant_npos)
150 {
151 set(std::forward<T>(value));
152 }
153
165 template <class T, typename std::enable_if<!std::is_same<T, variant &>::value, int>::type = 0>
166 variant &operator=(T &&value)
167 {
168 set(std::forward<T>(value));
169
170 return *this;
171 }
172
180 template <size_t I, class... ARGS>
181 typename variant_alternative<I, variant<TS...>>::type &emplace(ARGS &&...args);
182
190 template <class T, class... ARGS>
191 T &emplace(ARGS &&...args)
192 {
193 const constexpr auto newIndex = Detail::IndexOf<T, TS...>;
194 static_assert(newIndex != variant_npos, "CVB: assigned type not in alternatives list");
195
196 return emplace<newIndex>(std::forward<ARGS>(args)...);
197 }
198
202 size_t index() const noexcept
203 {
204 return index_;
205 }
206
212 void swap(variant &other);
213
219 bool valueless_by_exception() const noexcept
220 {
221 return index_ == variant_npos;
222 }
223 };
224
225#pragma region monostate
226
237 {
238 };
239
242 constexpr bool operator<(monostate, monostate) noexcept
243 {
244 return false;
245 }
246
249 constexpr bool operator>(monostate, monostate) noexcept
250 {
251 return false;
252 }
253
256 constexpr bool operator<=(monostate, monostate) noexcept
257 {
258 return true;
259 }
260
263 constexpr bool operator>=(monostate, monostate) noexcept
264 {
265 return true;
266 }
267
270 constexpr bool operator==(monostate, monostate) noexcept
271 {
272 return true;
273 }
274
277 constexpr bool operator!=(monostate, monostate) noexcept
278 {
279 return false;
280 }
281
282#pragma endregion
283
284#pragma region variant_alternative
285
286 template <size_t I, class... TS>
287 struct variant_alternative<I, variant<TS...>>
288 {
289 using type = Detail::TypeAt<I, TS...>;
290 };
291
292 template <size_t I, class T>
294 {
295 };
296
297 template <size_t I, class T>
298 struct variant_alternative<I, const T> : variant_alternative<I, typename std::remove_reference<T>::type>
299 {
300 };
301
302 template <size_t I, class T>
303 struct variant_alternative<I, volatile T> : variant_alternative<I, typename std::remove_reference<T>::type>
304 {
305 };
306
307 template <size_t I, class T>
308 struct variant_alternative<I, const volatile T> : variant_alternative<I, typename std::remove_reference<T>::type>
309 {
310 };
311
315 template <size_t I, class T>
317
318#pragma endregion
319
320#pragma region variant_size
321
326 template <class T>
328
329 template <class... TS>
330 struct variant_size<variant<TS...>> : Detail::SizeT<sizeof...(TS)>
331 {
332 };
333
334 template <class T>
335 struct variant_size<T &> : variant_size<T>
336 {
337 };
338
339 template <class T>
340 struct variant_size<const T> : variant_size<typename std::remove_reference<T>::type>
341 {
342 };
343
344 template <class T>
345 struct variant_size<volatile T> : variant_size<typename std::remove_reference<T>::type>
346 {
347 };
348
349 template <class T>
350 struct variant_size<const volatile T> : variant_size<typename std::remove_reference<T>::type>
351 {
352 };
353
358 template <class T>
360
361#pragma endregion
362
363#pragma region get<I>
364
376
381 template <size_t I, class... TS>
382 const variant_alternative_t<I, variant<TS...>> &get(const variant<TS...> &var)
383 {
384 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
385 if (I != var.index())
386 throw bad_variant_access{};
387
388 return reinterpret_cast<const variant_alternative_t<I, variant<TS...>> &>(var.data_);
389 }
390
395 template <size_t I, class... TS>
397 {
398 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
399 if (I != var.index())
400 throw bad_variant_access{};
401
402 return reinterpret_cast<variant_alternative_t<I, variant<TS...>> &>(var.data_);
403 }
404
409 template <size_t I, class... TS>
411 {
412 static_assert(I < sizeof...(TS), "CVB: alternative index out of range");
413 if (I != var.index())
414 throw bad_variant_access{};
415
416 return reinterpret_cast<variant_alternative_t<I, variant<TS...>> &&>(var.data_);
417 }
418
419#pragma endregion
420
421#pragma region get<T>
422
434
439 template <class T, class... TS>
440 const T &get(const variant<TS...> &var)
441 {
442 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
443 static_assert(index != variant_npos, "CVB: type not in alternatives list");
444
445 return get<index>(var);
446 }
447
452 template <class T, class... TS>
454 {
455 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
456 static_assert(index != variant_npos, "CVB: type not in alternatives list");
457
458 return get<index>(var);
459 }
460
465 template <class T, class... TS>
467 {
468 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
469 static_assert(index != variant_npos, "CVB: type not in alternatives list");
470
471 return get<index>(std::move(var));
472 }
473
474#pragma endregion
475
476#pragma region get_if<I>
477
489 template <size_t I, class... TS>
490 typename std::add_pointer<const variant_alternative_t<I, variant<TS...>>>::type
491 get_if(const variant<TS...> *pvar) noexcept
492 {
493 if (!pvar || pvar->index() != I)
494 return nullptr;
495
496 return &get<I>(*pvar);
497 }
498
503 template <size_t I, class... TS>
504 typename std::add_pointer<variant_alternative_t<I, variant<TS...>>>::type get_if(variant<TS...> *pvar) noexcept
505 {
506 if (!pvar || pvar->index() != I)
507 return nullptr;
508
509 return &get<I>(*pvar);
510 }
511
512#pragma endregion
513
514#pragma region get_if<I>
515
527
532 template <class T, class... TS>
534 {
535 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
536 static_assert(index != variant_npos, "CVB: type not in alternatives list");
537
538 return get_if<index>(pvar);
539 }
540
545 template <class T, class... TS>
547 {
548 static const constexpr size_t index = Detail::IndexOf<T, TS...>;
549 static_assert(index != variant_npos, "CVB: type not in alternatives list");
550
551 return get_if<index>(pvar);
552 }
553
554#pragma endregion
555
556#pragma region holds_alternative
557
566 template <class T, class... TS>
567 bool holds_alternative(const variant<TS...> &var) noexcept
568 {
569 static_assert(Detail::Contains<T, TS...>, "CVB: alternative is not contained in variant.");
570 return var.index() == Detail::IndexOf<T, TS...>;
571 }
572
573#pragma endregion
574
575#pragma region visit
576
577#pragma region VisitHelper
578
579 namespace Detail
580 {
581
583 template <class T, size_t COUNT, size_t I = 0>
584 struct VisitHelper1
585 {
586 template <class VISITOR, class VARIANT>
587 T operator()(VISITOR &&visitor, VARIANT &&var) const
588 {
589 if (I == var.index())
590 return visitor(get<I>(std::forward<VARIANT>(var)));
591 else
592 return VisitHelper1<T, COUNT, I + 1>{}(std::forward<VISITOR>(visitor), std::forward<VARIANT>(var));
593 }
594 };
595
596 template <class T, size_t COUNT>
597 struct VisitHelper1<T, COUNT, COUNT>
598 {
599 template <class VISITOR, class VARIANT>
600 T operator()(VISITOR &&, VARIANT &&) const
601 {
602 throw bad_variant_access{};
603 }
604 };
605
606 } // namespace Detail
607
608#pragma endregion
609
621 template <class VISITOR, class VARIANT>
622 auto visit(VISITOR &&visitor, VARIANT &&var) -> decltype(visitor(get<0>(var)))
623 {
624 using RESULT = decltype(visitor(get<0>(var)));
625 using HELPER = Detail::VisitHelper1<RESULT, variant_alternatives_size_v<VARIANT>>;
626
627 return HELPER{}(std::forward<VISITOR>(visitor), std::forward<VARIANT>(var));
628 }
629
630#pragma endregion
631
632#pragma region comparison operators
633
644 template <class... TS>
645 bool operator==(const variant<TS...> &lhs, const variant<TS...> &rhs)
646 {
647 return lhs.index() == rhs.index()
648 && (lhs.valueless_by_exception() || visit(Detail::VariantEqual{&lhs.data_}, rhs));
649 }
650
661 template <class... TS>
662 bool operator!=(const variant<TS...> &lhs, const variant<TS...> &rhs)
663 {
664 return !(lhs == rhs);
665 }
666
680 template <class... TS>
681 bool operator<(const variant<TS...> &lhs, const variant<TS...> &rhs)
682 {
683 return !rhs.valueless_by_exception()
684 && (lhs.valueless_by_exception() || lhs.index() < rhs.index()
685 || (lhs.index() == rhs.index() && visit(Detail::VariantLess{&lhs.data_}, rhs)));
686 }
687
696 template <class... TS>
697 bool operator<=(const variant<TS...> &lhs, const variant<TS...> &rhs)
698 {
699 return !(rhs < lhs);
700 }
701
710 template <class... TS>
711 bool operator>(const variant<TS...> &lhs, const variant<TS...> &rhs)
712 {
713 return rhs < lhs;
714 }
715
724 template <class... TS>
725 bool operator>=(const variant<TS...> &lhs, const variant<TS...> &rhs)
726 {
727 return !(lhs < rhs);
728 }
729
730#pragma endregion
731
732#pragma region variant copy
733
734 template <class... TS>
736 : index_(rhs.index_)
737 {
738 visit(Detail::VariantCopier{&data_}, rhs);
739 }
740
741 template <class... TS>
743 {
744 if (this != &rhs)
745 {
746 delete_if_valid();
747 visit(Detail::VariantCopier{&data_}, rhs);
748 index_ = rhs.index_;
749 }
750
751 return *this;
752 }
753
754#pragma endregion
755
756#pragma region variant move
757
758 template <class... TS>
760 : index_(rhs.index_)
761 {
762 try
763 {
764 visit(Detail::VariantMover{&data_}, std::move(rhs));
765 rhs.delete_if_valid(); // NOLINT(bugprone-use-after-move)
766 }
767 catch (...)
768 {
769 // will never throw, but visit is capable of thowing so we must satisfy the compiler.
770 }
771 }
772
773 template <class... TS>
775 {
776 if (this != &rhs)
777 {
778 delete_if_valid();
779 visit(Detail::VariantMover{&data_}, std::move(rhs));
780 index_ = rhs.index_;
781 rhs.delete_if_valid();
782 }
783
784 return *this;
785 }
786
787#pragma endregion
788
789#pragma region variant swap
790
791 template <class... TS>
793 {
794 // TODO: this can be made more efficient when we have the possibility to visit
795 // multiple variants.
796 auto old = std::move(*this);
797 *this = std::move(other);
798 other = std::move(old);
799 }
800
801#pragma endregion
802
803#pragma region variant::delete_if_valid
804
806 template <class... TS>
807 void variant<TS...>::delete_if_valid() noexcept
808 {
809 try
810 {
811 if (!valueless_by_exception())
812 {
813 visit(Detail::VariantDestructor{}, *this);
814 index_ = variant_npos;
815 }
816 }
817 catch (...)
818 {
819 // will never throw, but visit is capable of thowing so we must satisfy the compiler.
820 }
821 }
822
823#pragma endregion
824
825#pragma region variant::emplace<I>
826
827 template <class... TS>
828 template <size_t I, class... ARGS>
829 typename variant_alternative<I, variant<TS...>>::type &variant<TS...>::emplace(ARGS &&...args)
830 {
831 using NewT = Detail::TypeAt<I, TS...>;
832
833 delete_if_valid();
834 auto result = new (&data_) NewT(std::forward<ARGS>(args)...); // NOLINT(cppcoreguidelines-owning-memory)
835 index_ = I;
836
837 return *result;
838 }
839
840#pragma endregion
841
842 } // namespace Shims
843
844 CVB_END_INLINE_NS
845
846} // namespace Cvb
847
848namespace std // NOLINT(cert-dcl58-cpp)
849{
851 template <class... TS>
852 void swap(Cvb::Shims::variant<TS...> &lhs, Cvb::Shims::variant<TS...> &rhs) // NOLINT(cert-dcl58-cpp)
853 {
854 lhs.swap(rhs);
855 }
856} // namespace std
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:742
size_t index() const noexcept
Gets the zero-based index of the alternative held by this instance.
Definition variant.hpp:202
bool valueless_by_exception() const noexcept
Returns false only if this instance holds a value.
Definition variant.hpp:219
bool operator<=(const variant< TS... > &lhs, const variant< TS... > &rhs)
Less-than or equal operator for Shims::variant object.
Definition variant.hpp:697
variant() noexcept
Default ctor creating a variant with default value of the first alternative type.
Definition variant.hpp:99
variant(T &&value)
Ctor from an object of supported type T.
Definition variant.hpp:148
variant & operator=(T &&value)
Assignment from an object of supported type T.
Definition variant.hpp:166
static const constexpr size_t variant_alternatives_size_v
Get the number of alternatives in a variant.
Definition variant.hpp:359
std::add_pointer< constT >::type get_if(const variant< TS... > *pvar) noexcept
Tries to get the value of the given Shims::variant pointer pvar as the alternative with type T.
Definition variant.hpp:533
bool operator>(const variant< TS... > &lhs, const variant< TS... > &rhs)
Greater-than operator for Shims::variant object.
Definition variant.hpp:711
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:622
~variant() noexcept
Destructor.
Definition variant.hpp:106
void swap(variant &other)
Swaps the content of this variant with the other one.
Definition variant.hpp:792
std::add_pointer< T >::type get_if(variant< TS... > *pvar) noexcept
Tries to get the value of the given Shims::variant pointer pvar as the alternative with type T.
Definition variant.hpp:546
T & emplace(ARGS &&...args)
Emplaces an object as a type T.
Definition variant.hpp:191
const variant_alternative_t< I, variant< TS... > > & get(const variant< TS... > &var)
Gets the value of the given Shims::variant var as the alternative with index I.
Definition variant.hpp:382
bool operator<(const variant< TS... > &lhs, const variant< TS... > &rhs)
Less than operator for Shims::variant objects.
Definition variant.hpp:681
std::add_pointer< variant_alternative_t< I, variant< TS... > > >::type get_if(variant< TS... > *pvar) noexcept
Tries to get the value of the given Shims::variant pointer pvar as the alternative with index I.
Definition variant.hpp:504
T get(variant< TS... > &&var)
Gets the value of the given Shims::variant var as the alternative with type T.
Definition variant.hpp:466
variant_alternative_t< I, variant< TS... > > get(variant< TS... > &&var)
Gets the value of the given Shims::variant var as the alternative with index I.
Definition variant.hpp:410
variant_alternative< I, variant< TS... > >::type & emplace(ARGS &&...args)
Emplaces an object as alternative at index I.
Definition variant.hpp:829
bool operator>=(const variant< TS... > &lhs, const variant< TS... > &rhs)
Greater-than or equal operator for Shims::variant object.
Definition variant.hpp:725
bool operator!=(const variant< TS... > &lhs, const variant< TS... > &rhs)
Inequality operator for Shims::variant objects.
Definition variant.hpp:662
variant_alternative_t< I, variant< TS... > > & get(variant< TS... > &var)
Gets the value of the given Shims::variant var as the alternative with index I.
Definition variant.hpp:396
T & get(variant< TS... > &var)
Gets the value of the given Shims::variant var as the alternative with type T.
Definition variant.hpp:453
bool holds_alternative(const variant< TS... > &var) noexcept
Gets whether the Shims::variant var holds an instance of type T.
Definition variant.hpp:567
bool operator==(const variant< TS... > &lhs, const variant< TS... > &rhs)
Equality operator for Shims::variant objects.
Definition variant.hpp:645
const T & get(const variant< TS... > &var)
Gets the value of the given Shims::variant var as the alternative with type T.
Definition variant.hpp:440
bad_variant_access()=default
Default ctor.
T forward(T... args)
T move(T... args)
Namespace to for standard types that are not available in C++14.
Definition variant_helper.hpp:20
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:316
Root namespace for the Image Manager interface.
Definition c_bayer_to_rgb.h:17
static const constexpr size_t variant_npos
Returned by variant::index() on invalid state.
Definition variant.hpp:33
Unit type intended for use as a well-behaved empty alternative in Shims::variant.
Definition variant.hpp:237
constexpr bool operator>=(monostate, monostate) noexcept
Definition variant.hpp:263
constexpr bool operator!=(monostate, monostate) noexcept
Definition variant.hpp:277
constexpr bool operator<(monostate, monostate) noexcept
Definition variant.hpp:242
constexpr bool operator==(monostate, monostate) noexcept
Definition variant.hpp:270
constexpr bool operator<=(monostate, monostate) noexcept
Definition variant.hpp:256
constexpr bool operator>(monostate, monostate) noexcept
Definition variant.hpp:249
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:327