variant.hpp
1 #pragma once
2 
9 #include <new>
10 #include <exception>
11 #include <utility>
12 #include "_detail/variant_helper.hpp"
13 
14 namespace 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 = 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 
226 struct monostate {};
227 
230 constexpr bool operator<(monostate, monostate) noexcept { return false; }
231 
234 constexpr bool operator>(monostate, monostate) noexcept { return false; }
235 
238 constexpr bool operator<=(monostate, monostate) noexcept { return true; }
239 
242 constexpr bool operator>=(monostate, monostate) noexcept { return true; }
243 
246 constexpr bool operator==(monostate, monostate) noexcept { return true; }
247 
250 constexpr bool operator!=(monostate, monostate) noexcept { return false; }
251 
252 #pragma endregion
253 
254 #pragma region variant_alternative
255 
256 template <size_t I, class... TS>
257 struct variant_alternative<I, variant<TS...>>
258 {
259  using type = Detail::TypeAt<I, TS...>;
260 };
261 
262 template <size_t I, class T>
264 
265 template <size_t I, class T>
266 struct variant_alternative<I, const T> : variant_alternative<I, typename std::remove_reference<T>::type> {};
267 
268 template <size_t I, class T>
269 struct variant_alternative<I, volatile T> : variant_alternative<I, typename std::remove_reference<T>::type> {};
270 
271 template <size_t I, class T>
272 struct variant_alternative<I, const volatile T> : variant_alternative<I, typename std::remove_reference<T>::type> {};
273 
277 template <size_t I, class T>
279 
280 #pragma endregion
281 
282 #pragma region variant_size
283 
288 template <class T>
290 
291 template <class... TS>
292 struct variant_size<variant<TS...>> : Detail::SizeT<sizeof...(TS)> {};
293 
294 template <class T>
295 struct variant_size<T &> : variant_size<T> {};
296 
297 template <class T>
298 struct variant_size<const T> : variant_size<typename std::remove_reference<T>::type> {};
299 
300 template <class T>
301 struct variant_size<volatile T> : variant_size<typename std::remove_reference<T>::type> {};
302 
303 template <class T>
304 struct variant_size<const volatile T> : variant_size<typename std::remove_reference<T>::type> {};
305 
310 template <class T>
311 static const constexpr size_t variant_alternatives_size_v = variant_size<T>::value;
312 
313 #pragma endregion
314 
315 #pragma region get<I>
316 
333 template <size_t I, class... TS>
334 const 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 
347 template <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 
361 template <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 
391 template <class T, class... TS>
392 const 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 
404 template <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 
417 template <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 
441 template <size_t I, class... TS>
442 typename 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 
454 template <size_t I, class... TS>
455 typename 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 
483 template <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 
496 template <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 
517 template <class T, class... TS>
518 bool 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 
530 namespace Detail
531 {
532 
534 template <class T, size_t COUNT, size_t I = 0>
535 struct 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 
547 template <class T, size_t COUNT>
548 struct 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 
572 template <class VISITOR, class VARIANT>
573 auto 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 
595 template <class... TS>
596 bool 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 
612 template <class... TS>
613 bool operator !=(const variant<TS...> &lhs, const variant<TS...> &rhs)
614 {
615  return !(lhs == rhs);
616 }
617 
631 template <class... TS>
632 bool 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 
650 template <class... TS>
651 bool operator <=(const variant<TS...> &lhs, const variant<TS...> &rhs)
652 {
653  return !(rhs < lhs);
654 }
655 
656 
665 template <class... TS>
666 bool operator >(const variant<TS...> &lhs, const variant<TS...> &rhs)
667 {
668  return rhs < lhs;
669 }
670 
671 
680 template <class... TS>
681 bool 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 
690 template <class... TS>
692  : index_(rhs.index_)
693 {
694  visit(Detail::VariantCopier{&data_}, rhs);
695 }
696 
697 template <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 
714 template <class... TS>
716  : index_(rhs.index_)
717 {
718  visit(Detail::VariantMover{&data_}, std::move(rhs));
719  rhs.delete_if_valid();
720 }
721 
722 template <class... TS>
724 {
725  if(this != &rhs)
726  {
727  delete_if_valid();
728  visit(Detail::VariantMover{&data_}, std::move(rhs));
729  index_ = rhs.index_;
730  rhs.delete_if_valid();
731  }
732 
733  return *this;
734 }
735 
736 #pragma endregion
737 
738 #pragma region variant swap
739 
740 template <class... TS>
742 {
743  // TODO: this can be made more efficient when we have the possibility to visit
744  // multiple variants.
745  auto old = std::move(*this);
746  *this = std::move(other);
747  other = std::move(old);
748 }
749 
750 #pragma endregion
751 
752 #pragma region variant::delete_if_valid
753 
755 template <class... TS>
756 void variant<TS...>::delete_if_valid() noexcept
757 {
758  if(!valueless_by_exception())
759  {
760  visit(Detail::VariantDestructor{}, *this);
761  index_ = variant_npos;
762  }
763 }
764 
765 #pragma endregion
766 
767 #pragma region variant::emplace<I>
768 
769 template <class... TS>
770 template <size_t I, class... ARGS>
771 typename variant_alternative<I, variant<TS...>>::type & variant<TS...>::emplace(ARGS &&... args)
772 {
773  using NewT = Detail::TypeAt<I, TS...>;
774 
775  delete_if_valid();
776  auto result = new(&data_) NewT(std::forward<ARGS>(args)...);
777  index_ = I;
778 
779  return *result;
780 }
781 
782 #pragma endregion
783 
784 } // namespace Shims
785 
786 CVB_END_INLINE_NS
787 
788 } // namespace Cvb
789 
790 namespace std
791 {
793  template <class... TS>
795  {
796  lhs.swap(rhs);
797  }
798 }
This class is a replacement for C++17 std::variant.
Definition: variant.hpp:48
static const constexpr size_t variant_npos
Returned by variant::index() on invalid state.
Definition: variant.hpp:33
constexpr bool operator>=(monostate, monostate) noexcept
Definition: variant.hpp:242
variant() noexcept
Default ctor creating a variant with default value of the first alternative type.
Definition: variant.hpp:98
constexpr bool operator!=(monostate, monostate) noexcept
Definition: variant.hpp:250
bool operator >(const variant< TS... > &lhs, const variant< TS... > &rhs)
Greater-than operator for Shims::variant object.
Definition: variant.hpp:666
constexpr bool operator==(monostate, monostate) noexcept
Definition: variant.hpp:246
std::add_pointer< const T >::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:484
bool operator !=(const variant< TS... > &lhs, const variant< TS... > &rhs)
Inequality operator for Shims::variant objects.
Definition: variant.hpp:613
bad_variant_access()=default
Default ctor.
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:455
bool operator >=(const variant< TS... > &lhs, const variant< TS... > &rhs)
Greater-than or equal operator for Shims::variant object.
Definition: variant.hpp:681
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:362
void swap(variant &other)
Swaps the content of this variant with the other one.
Definition: variant.hpp:741
~variant() noexcept
Destructor.
Definition: variant.hpp:105
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:497
T & get(variant< TS... > &var)
Gets the value of the given Shims::variant var as the alternative with type T.
Definition: variant.hpp:405
constexpr bool operator<=(monostate, monostate) noexcept
Definition: variant.hpp:238
constexpr bool operator<(monostate, monostate) noexcept
Definition: variant.hpp:230
Root namespace for the Image Manager interface.
Definition: version.hpp:11
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:334
typename variant_alternative< I, T >::type variant_alternative_t
Helper type alias for Shims::variant_alternative.
Definition: variant.hpp:278
variant_alternative< I, variant< TS... > >::type & emplace(ARGS &&... args)
Emplaces an object as alternative at index I.
Definition: variant.hpp:771
STL class.
variant(T &&value)
Ctor from an object of supported type T.
Definition: variant.hpp:144
bool holds_alternative(const variant< TS... > &var) noexcept
Gets whether the Shims::variant var holds an instance of type T.
Definition: variant.hpp:518
Error when accessing not set alternative.
Definition: variant.hpp:25
Get the number of alternatives in a variant.
Definition: variant.hpp:289
constexpr bool operator>(monostate, monostate) noexcept
Definition: variant.hpp:234
size_t index() const noexcept
Gets the zero-based index of the alternative held by this instance.
Definition: variant.hpp:198
T & emplace(ARGS &&... args)
Emplaces an object as a type T.
Definition: variant.hpp:187
T get(variant< TS... > &&var)
Gets the value of the given Shims::variant var as the alternative with type T.
Definition: variant.hpp:418
variant & operator=(const variant &rhs)
Copy operator.
Definition: variant.hpp:698
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:392
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
Compile-time index access to the type of the alternatives at index I.
Definition: variant.hpp:41
Unit type intended for use as a well-behaved empty alternative in Shims::variant.
Definition: variant.hpp:226
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:348
bool valueless_by_exception() const noexcept
Returns false only if this instance holds a value.
Definition: variant.hpp:212