libdonut  2.3.2
Application framework for cross-platform game development in C++20
Variant.hpp
Go to the documentation of this file.
1 #ifndef DONUT_VARIANT_HPP
2 #define DONUT_VARIANT_HPP
3 
4 #include <cassert> // assert
5 #include <compare> // std::strong_ordering, std::common_comparison_category_t, std::compare_three_way_result_t
6 #include <cstddef> // std::size_t, std::byte
7 #include <cstdint> // std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t
8 #include <exception> // std::exception
9 #include <functional> // std::hash, std::invoke
10 #include <initializer_list> // std::initializer_list
11 #include <memory> // std::construct_at, std::destroy_at
12 #include <new> // std::launder
13 #include <optional> // std::optional
14 #include <type_traits> // std::is_..._v, std::false_type, std::true_type, std::integral_constant, std::remove_..._t, std::common_type_t
15 #include <utility> // std::move, std::forward, std::swap, std::in_place_..., std::...index_sequence
16 
17 namespace donut {
18 
27 template <typename... Ts>
28 class Variant;
29 
30 namespace detail {
31 
32 template <auto...>
33 struct ConstantList {};
34 
35 template <std::size_t N, typename Union, auto... MemberPointers>
36 struct get_n_member_pointers {
37  using type = ConstantList<MemberPointers..., &Union::head>;
38 };
39 
40 template <std::size_t N, typename Union, auto... MemberPointers>
41 requires(N > 0) struct get_n_member_pointers<N, Union, MemberPointers...> : get_n_member_pointers<N - 1, typename Union::Tail, MemberPointers..., &Union::tail> {};
42 
43 template <std::size_t N, typename Union>
44 using get_n_member_pointers_t = typename get_n_member_pointers<N, Union>::type;
45 
46 template <auto... MemberPointers, typename U>
47 [[nodiscard]] constexpr auto& getUnionMemberImpl(ConstantList<MemberPointers...>, U&& u) noexcept {
48  return (std::forward<U>(u).*....*MemberPointers);
49 }
50 
51 template <std::size_t Index, typename U>
52 [[nodiscard]] constexpr auto& getUnionMember(U& u) noexcept {
53  return getUnionMemberImpl(get_n_member_pointers_t<Index, U>{}, u);
54 }
55 
56 template <typename... Ts>
57 union UnionStorage {
58  UnionStorage() = default;
59 
60  template <std::size_t Index, typename... Args>
61  UnionStorage(std::in_place_index_t<Index>, Args&&...) = delete; // NOLINT(cppcoreguidelines-missing-std-forward)
62 };
63 
64 template <typename First, typename... Rest>
65 union UnionStorage<First, Rest...> {
66  static constexpr std::size_t size = 1 + sizeof...(Rest);
67 
68  using Head = First;
69  using Tail = UnionStorage<Rest...>;
70 
71  constexpr UnionStorage()
72  : tail() {}
73 
74  template <typename... Args>
75  constexpr UnionStorage(std::in_place_index_t<0>, Args&&... args)
76  : head(std::forward<Args>(args)...) {}
77 
78  template <std::size_t Index, typename... Args>
79  constexpr UnionStorage(std::in_place_index_t<Index>, Args&&... args)
80  : tail(std::in_place_index<Index - 1>, std::forward<Args>(args)...) {}
81 
82  constexpr UnionStorage(const UnionStorage& other, std::size_t other_index) {
83  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
84  (void)(((other_index == Indices) ? (create<Indices>(getUnionMember<Indices>(other)), true) : false) || ...);
85  }(std::make_index_sequence<size>{});
86  }
87 
88  constexpr UnionStorage(UnionStorage&& other, std::size_t other_index) { // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
89  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
90  (void)(((other_index == Indices) ? (create<Indices>(std::move(getUnionMember<Indices>(other))), true) : false) || ...);
91  }(std::make_index_sequence<size>{});
92  }
93 
94  ~UnionStorage() = default;
95  constexpr ~UnionStorage() requires(!std::is_trivially_destructible_v<Head> || !std::is_trivially_destructible_v<Tail>) {}
96 
97  UnionStorage(const UnionStorage&) = default;
98  UnionStorage(UnionStorage&&) = default;
99  UnionStorage& operator=(const UnionStorage&) = default;
100  UnionStorage& operator=(UnionStorage&&) = default;
101 
102  template <std::size_t Index, typename... Args>
103  constexpr void create(Args&&... args) {
104  std::construct_at(&getUnionMember<Index>(*this), std::forward<Args>(args)...);
105  }
106 
107  constexpr void destroy(std::size_t index) noexcept {
108  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
109  (void)(((index == Indices) ? (std::destroy_at(&getUnionMember<Indices>(*this)), true) : false) || ...);
110  }(std::make_index_sequence<size>{});
111  }
112 
113  Head head;
114  Tail tail;
115 };
116 
117 template <template <typename...> typename Template, typename... TemplateArgs>
118 constexpr void derivedFromTemplateSpecializationTest(const Template<TemplateArgs...>&);
119 
120 template <typename T, template <typename...> typename Template>
121 concept derived_from_template_specialization_of = requires(const T& t) { derivedFromTemplateSpecializationTest<Template>(t); };
122 
123 template <typename T, std::size_t Index, typename... Ts>
124 struct VariantIndexImpl;
125 
126 template <typename T, std::size_t Index, typename First, typename... Rest>
127 struct VariantIndexImpl<T, Index, First, Rest...> : VariantIndexImpl<T, Index + 1, Rest...> {};
128 
129 template <typename T, std::size_t Index, typename... Rest>
130 struct VariantIndexImpl<T, Index, T, Rest...> : std::integral_constant<std::size_t, Index> {};
131 
132 template <typename... Functors>
133 struct Overloaded : Functors... {
134  using Functors::operator()...;
135 };
136 
137 template <typename... Functors>
138 Overloaded(Functors...) -> Overloaded<Functors...>;
139 
140 template <typename V>
141 struct Matcher {
142  V variant;
143 
144  template <typename... Functors>
145  constexpr decltype(auto) operator()(Functors&&... functors) const {
146  return visit(detail::Overloaded{std::forward<Functors>(functors)...}, variant);
147  }
148 };
149 
150 } // namespace detail
151 
153 template <typename T, typename V>
154 struct variant_has_alternative;
155 
156 template <typename T, typename First, typename... Rest>
157 struct variant_has_alternative<T, Variant<First, Rest...>> : variant_has_alternative<T, Variant<Rest...>> {};
158 
159 template <typename T>
160 struct variant_has_alternative<T, Variant<>> : std::false_type {};
161 
162 template <typename T, typename... Rest>
163 struct variant_has_alternative<T, Variant<T, Rest...>> : std::true_type {};
165 
172 template <typename T, typename V>
173 inline constexpr bool variant_has_alternative_v = variant_has_alternative<T, V>::value;
174 
176 template <typename T, typename V>
177 struct variant_index;
178 
179 template <typename T, typename... Ts>
180 struct variant_index<T, Variant<Ts...>> : detail::VariantIndexImpl<T, 0, Ts...> {};
182 
189 template <typename T, typename V>
190 inline constexpr std::size_t variant_index_v = variant_index<T, V>::value;
191 
193 template <std::size_t Index, typename V>
194 struct variant_alternative;
195 
196 template <std::size_t Index, typename First, typename... Rest>
197 struct variant_alternative<Index, Variant<First, Rest...>> : variant_alternative<Index - 1, Variant<Rest...>> {};
198 
199 template <typename T, typename... Rest>
200 struct variant_alternative<0, Variant<T, Rest...>> {
201  using type = T;
202 };
204 
211 template <std::size_t Index, typename V>
212 using variant_alternative_t = typename variant_alternative<Index, V>::type;
213 
215 template <typename V>
216 struct variant_size;
217 
218 template <typename... Ts>
219 struct variant_size<Variant<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};
221 
227 template <typename V>
228 inline constexpr std::size_t variant_size_v = variant_size<V>::value;
229 
236 struct Monostate {};
237 
246 constexpr bool operator==(Monostate, Monostate) noexcept {
247  return true;
248 }
249 
258 constexpr std::strong_ordering operator<=>(Monostate, Monostate) noexcept {
259  return std::strong_ordering::equal;
260 }
261 
262 } // namespace donut
263 
267 template <>
268 class std::hash<donut::Monostate> {
269 public:
270  [[nodiscard]] std::size_t operator()(const donut::Monostate&) const {
271  return 0;
272  }
273 };
274 
275 namespace donut {
276 
282 struct BadVariantAccess : std::exception {
283  BadVariantAccess() noexcept = default;
284 
285  [[nodiscard]] const char* what() const noexcept override {
286  return "Bad variant access.";
287  }
288 };
289 
290 template <typename... Ts>
291 class Variant {
292 private:
293  static constexpr bool HAS_DEFAULT_CONSTRUCTOR = std::is_default_constructible_v<variant_alternative_t<0, Variant>>;
294  static constexpr bool HAS_COPY_CONSTRUCTOR = (std::is_copy_constructible_v<Ts> && ...);
295  static constexpr bool HAS_MOVE_CONSTRUCTOR = (std::is_move_constructible_v<Ts> && ...);
296  static constexpr bool HAS_COPY_ASSIGNMENT = ((std::is_copy_constructible_v<Ts> && std::is_copy_assignable_v<Ts>)&&...);
297  static constexpr bool HAS_MOVE_ASSIGNMENT = ((std::is_move_constructible_v<Ts> && std::is_move_assignable_v<Ts>)&&...);
298  static constexpr bool HAS_TRIVIAL_COPY_CONSTRUCTOR = (std::is_trivially_copy_constructible_v<Ts> && ...);
299  static constexpr bool HAS_TRIVIAL_MOVE_CONSTRUCTOR = (std::is_trivially_move_constructible_v<Ts> && ...);
300  static constexpr bool HAS_TRIVIAL_COPY_ASSIGNMENT =
301  ((std::is_trivially_copy_constructible_v<Ts> && std::is_trivially_copy_assignable_v<Ts> && std::is_trivially_destructible_v<Ts>)&&...);
302  static constexpr bool HAS_TRIVIAL_MOVE_ASSIGNMENT =
303  ((std::is_trivially_move_constructible_v<Ts> && std::is_trivially_move_assignable_v<Ts> && std::is_trivially_destructible_v<Ts>)&&...);
304  static constexpr bool HAS_TRIVIAL_DESTRUCTOR = (std::is_trivially_destructible_v<Ts> && ...);
305 
306  template <typename T>
307  struct SelectAlternativeTypeOverload {
308  template <typename U>
309  T operator()(T, U&& u) requires(requires { T{std::forward<U>(u)}; });
310  };
311 
312  struct SelectAlternativeTypeOverloadSet : SelectAlternativeTypeOverload<Ts>... {
313  using SelectAlternativeTypeOverload<Ts>::operator()...;
314  };
315 
316  template <typename U>
317  static constexpr auto F(U&& u) -> decltype(SelectAlternativeTypeOverloadSet{}(std::forward<U>(u), std::forward<U>(u)));
318 
319 public:
320  // clang-format off
328  using index_type =
329  std::conditional_t<sizeof...(Ts) < 255ull, std::uint8_t,
330  std::conditional_t<sizeof...(Ts) < 65535ull, std::uint16_t,
331  std::conditional_t<sizeof...(Ts) < 4294967295ull, std::uint32_t,
332  std::uint64_t>>>;
333  // clang-format on
334 
338  static constexpr index_type npos = sizeof...(Ts);
339 
347  constexpr Variant() noexcept(std::is_nothrow_default_constructible_v<variant_alternative_t<0, Variant>>) requires(HAS_DEFAULT_CONSTRUCTOR)
348  : Variant(std::in_place_index<0>) {}
349 
362  template <typename U>
363  constexpr Variant(U&& value) noexcept(std::is_nothrow_constructible_v<decltype(F(std::forward<U>(value)))>)
364  requires(!std::is_same_v<std::remove_cvref_t<U>, Variant> && variant_has_alternative_v<decltype(F(std::forward<U>(value))), Variant> &&
365  std::is_constructible_v<decltype(F(std::forward<U>(value))), U>)
366  : Variant(std::in_place_index<variant_index_v<decltype(F(std::forward<U>(value))), Variant>>, std::forward<U>(value)) {}
367 
379  template <typename T, typename... Args>
380  constexpr explicit Variant(std::in_place_type_t<T> type, Args&&... args) requires(variant_has_alternative_v<T, Variant> && std::is_constructible_v<T, Args...>)
381  : Variant(std::in_place_index<variant_index_v<T, Variant>>, std::forward<Args>(args)...) {
382  (void)type;
383  }
384 
400  template <typename T, typename U, typename... Args>
401  constexpr explicit Variant(std::in_place_type_t<T> type, std::initializer_list<U> ilist, Args&&... args)
402  requires(variant_has_alternative_v<T, Variant> && std::is_constructible_v<T, std::initializer_list<U>&, Args...>)
403  : Variant(std::in_place_index<variant_index_v<T, Variant>>, ilist, std::forward<Args>(args)...) {
404  (void)type;
405  }
406 
418  template <std::size_t Index, typename... Args>
419  constexpr explicit Variant(std::in_place_index_t<Index> index, Args&&... args)
420  requires(Index < sizeof...(Ts) && std::is_constructible_v<variant_alternative_t<Index, Variant>, Args...>)
421  : storage(index, std::forward<Args>(args)...)
422  , activeTypeIndex(Index) {}
423 
439  template <std::size_t Index, typename U, typename... Args>
440  constexpr explicit Variant(std::in_place_index_t<Index> index, std::initializer_list<U> ilist, Args&&... args)
441  requires(Index < sizeof...(Ts) && std::is_constructible_v<variant_alternative_t<Index, Variant>, std::initializer_list<U>&, Args...>)
442  : storage(index, ilist, std::forward<Args>(args)...)
443  , activeTypeIndex(Index) {}
444 
446  constexpr ~Variant() {
447  destroy();
448  }
449 
451  constexpr ~Variant() requires(HAS_TRIVIAL_DESTRUCTOR) = default;
452 
454  constexpr Variant(const Variant& other) requires(!HAS_COPY_CONSTRUCTOR) = delete;
455 
457  constexpr Variant(const Variant& other) requires(HAS_COPY_CONSTRUCTOR && HAS_TRIVIAL_COPY_CONSTRUCTOR) = default;
458 
460  constexpr Variant(const Variant& other) requires(HAS_COPY_CONSTRUCTOR && !HAS_TRIVIAL_COPY_CONSTRUCTOR)
461  : storage(other.storage, other.activeTypeIndex)
462  , activeTypeIndex(other.activeTypeIndex) {}
463 
465  constexpr Variant(Variant&& other) noexcept requires(HAS_MOVE_CONSTRUCTOR && HAS_TRIVIAL_MOVE_CONSTRUCTOR) = default;
466 
468  constexpr Variant(Variant&& other) noexcept(
469  (std::is_nothrow_move_constructible_v<Ts> && ...)) // NOLINT(performance-noexcept-move-constructor, cppcoreguidelines-noexcept-move-operations)
470  requires(HAS_MOVE_CONSTRUCTOR && !HAS_TRIVIAL_MOVE_CONSTRUCTOR)
471  : storage(std::move(other.storage), other.activeTypeIndex)
472  , activeTypeIndex(other.activeTypeIndex) {}
473 
475  constexpr Variant& operator=(const Variant& other) requires(!HAS_COPY_ASSIGNMENT) = delete;
476 
478  constexpr Variant& operator=(const Variant& other) requires(HAS_COPY_ASSIGNMENT && HAS_TRIVIAL_COPY_ASSIGNMENT) = default;
479 
486  constexpr Variant& operator=(const Variant& other) requires(HAS_COPY_ASSIGNMENT && !HAS_TRIVIAL_COPY_ASSIGNMENT) {
487  *this = Variant{other};
488  return *this;
489  }
490 
492  constexpr Variant& operator=(Variant&& other) noexcept requires(HAS_MOVE_ASSIGNMENT && HAS_TRIVIAL_MOVE_ASSIGNMENT) = default;
493 
500  constexpr Variant& operator=(Variant&& other) noexcept(
501  ((std::is_nothrow_move_constructible_v<Ts> && // NOLINT(performance-noexcept-move-constructor, cppcoreguidelines-noexcept-move-operations)
502  std::is_nothrow_move_assignable_v<Ts>)&&...)) requires(HAS_MOVE_ASSIGNMENT && !HAS_TRIVIAL_MOVE_ASSIGNMENT) {
503  if (activeTypeIndex == other.activeTypeIndex) {
504  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
505  (void)(((is<Indices>()) ? ((as<Indices>() = std::move(other.template as<Indices>())), true) : false) || ...);
506  }(std::make_index_sequence<sizeof...(Ts)>{});
507  } else if (other.activeTypeIndex == npos) {
508  destroy();
509  } else {
510  destroy();
511  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
512  (void)(((other.template is<Indices>()) ? ((storage.template create<Indices>(std::move(other.template as<Indices>()))), true) : false) || ...);
513  }(std::make_index_sequence<sizeof...(Ts)>{});
514  activeTypeIndex = other.activeTypeIndex;
515  }
516  return *this;
517  }
518 
534  template <typename U>
535  constexpr Variant& operator=(U&& value) noexcept(
536  std::is_nothrow_assignable_v<decltype(F(std::forward<U>(value)))&, U>&& std::is_nothrow_constructible_v<decltype(F(std::forward<U>(value))), U>)
537  requires(!std::is_same_v<std::remove_cvref_t<U>, Variant> && variant_has_alternative_v<decltype(F(std::forward<U>(value))), Variant> &&
538  std::is_assignable_v<decltype(F(std::forward<U>(value))), U>) {
539  using T = decltype(F(std::forward<U>(value)));
540  if (is<T>()) {
541  as<T>() = std::forward<U>(value);
542  } else {
543  if constexpr (std::is_nothrow_constructible_v<T, U> || !std::is_nothrow_move_constructible_v<T>) {
544  emplace<T>(std::forward<U>(value));
545  } else {
546  emplace<T>(T(std::forward<U>(value)));
547  }
548  }
549  return *this;
550  }
551 
566  template <typename T, typename... Args>
567  constexpr T& emplace(Args&&... args) requires(variant_has_alternative_v<T, Variant> && std::is_constructible_v<T, Args...>) {
568  return emplace<variant_index_v<T, Variant>>(std::forward<Args>(args)...);
569  }
570 
589  template <typename T, typename U, typename... Args>
590  constexpr T& emplace(std::initializer_list<U> ilist, Args&&... args)
591  requires(variant_has_alternative_v<T, Variant> && std::is_constructible_v<T, std::initializer_list<U>&, Args...>) {
592  return emplace<variant_index_v<T, Variant>>(ilist, std::forward<Args>(args)...);
593  }
594 
609  template <std::size_t Index, typename... Args>
610  constexpr variant_alternative_t<Index, Variant>& emplace(Args&&... args) requires(std::is_constructible_v<variant_alternative_t<Index, Variant>, Args...>) {
611  static_assert(Index < sizeof...(Ts));
612  destroy();
613  storage.template create<Index>(std::forward<Args>(args)...);
614  activeTypeIndex = Index;
615  return as<Index>();
616  }
617 
636  template <std::size_t Index, typename U, typename... Args>
637  constexpr variant_alternative_t<Index, Variant>& emplace(std::initializer_list<U> ilist, Args&&... args)
638  requires(std::is_constructible_v<variant_alternative_t<Index, Variant>, std::initializer_list<U>&, Args...>) {
639  static_assert(Index < sizeof...(Ts));
640  destroy();
641  storage.template create<Index>(ilist, std::forward<Args>(args)...);
642  activeTypeIndex = Index;
643  return as<Index>();
644  }
645 
658  constexpr void swap(Variant& other) noexcept(
659  ((std::is_nothrow_move_constructible_v<Ts> && std::is_nothrow_swappable_v<Ts>)&&...)) { // NOLINT(performance-noexcept-swap, cppcoreguidelines-noexcept-swap)
660  if (activeTypeIndex == other.activeTypeIndex) {
661  using std::swap;
662  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
663  (void)(((is<Indices>()) ? ((swap(as<Indices>(), other.template as<Indices>())), true) : false) || ...);
664  }(std::make_index_sequence<sizeof...(Ts)>{});
665  } else {
666  Variant old = std::move(*this);
667  *this = std::move(other);
668  other = std::move(old);
669  }
670  }
671 
685  friend constexpr void swap(Variant& a, Variant& b) noexcept(noexcept(a.swap(b))) { // NOLINT(performance-noexcept-swap, cppcoreguidelines-noexcept-swap)
686  a.swap(b);
687  }
688 
696  [[nodiscard]] constexpr index_type index() const noexcept {
697  return activeTypeIndex;
698  }
699 
706  [[nodiscard]] constexpr bool valueless_by_exception() const noexcept {
707  return activeTypeIndex == npos;
708  }
709 
719  template <typename T>
720  [[nodiscard]] constexpr bool is() const noexcept requires(variant_has_alternative_v<T, Variant>) {
721  return activeTypeIndex == variant_index_v<T, Variant>;
722  }
723 
734  template <std::size_t Index>
735  [[nodiscard]] constexpr bool is() const noexcept {
736  return activeTypeIndex == Index;
737  }
738 
763  template <typename T>
764  [[nodiscard]] constexpr T& as() & noexcept requires(variant_has_alternative_v<T, Variant>) {
765  assert(is<T>());
766  return as<variant_index_v<T, Variant>>();
767  }
768 
793  template <typename T>
794  [[nodiscard]] constexpr const T& as() const& noexcept requires(variant_has_alternative_v<T, Variant>) {
795  assert(is<T>());
796  return as<variant_index_v<T, Variant>>();
797  }
798 
823  template <typename T>
824  [[nodiscard]] constexpr T&& as() && noexcept requires(variant_has_alternative_v<T, Variant>) {
825  assert(is<T>());
826  return std::move(as<variant_index_v<T, Variant>>());
827  }
828 
853  template <typename T>
854  [[nodiscard]] constexpr const T&& as() const&& noexcept requires(variant_has_alternative_v<T, Variant>) {
855  assert(is<T>());
856  return std::move(as<variant_index_v<T, Variant>>());
857  }
858 
884  template <std::size_t Index>
885  [[nodiscard]] constexpr variant_alternative_t<Index, Variant>& as() & noexcept {
886  assert(is<Index>());
887  return detail::getUnionMember<Index>(storage);
888  }
889 
915  template <std::size_t Index>
916  [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>& as() const& noexcept {
917  assert(is<Index>());
918  return detail::getUnionMember<Index>(storage);
919  }
920 
946  template <std::size_t Index>
947  [[nodiscard]] constexpr variant_alternative_t<Index, Variant>&& as() && noexcept {
948  assert(is<Index>());
949  return std::move(detail::getUnionMember<Index>(storage));
950  }
951 
977  template <std::size_t Index>
978  [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>&& as() const&& noexcept {
979  assert(is<Index>());
980  return std::move(detail::getUnionMember<Index>(storage));
981  }
982 
997  template <typename T>
998  [[nodiscard]] constexpr T& get() &
999  requires(variant_has_alternative_v<T, Variant>) {
1000  if (!is<T>()) {
1001  throw BadVariantAccess{};
1002  }
1003  return as<T>();
1004  }
1005 
1020  template <typename T>
1021  [[nodiscard]] constexpr const T& get() const& requires(variant_has_alternative_v<T, Variant>) {
1022  if (!is<T>()) {
1023  throw BadVariantAccess{};
1024  }
1025  return as<T>();
1026  }
1027 
1042  template <typename T>
1043  [[nodiscard]] constexpr T&& get() &&
1044  requires(variant_has_alternative_v<T, Variant>) {
1045  if (!is<T>()) {
1046  throw BadVariantAccess{};
1047  }
1048  return std::move(as<T>());
1049  }
1050 
1065  template <typename T>
1066  [[nodiscard]] constexpr const T&& get() const&& requires(variant_has_alternative_v<T, Variant>) {
1067  if (!is<T>()) {
1068  throw BadVariantAccess{};
1069  }
1070  return std::move(as<T>());
1071  }
1072 
1088  template <std::size_t Index>
1089  [[nodiscard]] constexpr variant_alternative_t<Index, Variant>& get() & {
1090  if (!is<Index>()) {
1091  throw BadVariantAccess{};
1092  }
1093  return as<Index>();
1094  }
1095 
1111  template <std::size_t Index>
1112  [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>& get() const& {
1113  if (!is<Index>()) {
1114  throw BadVariantAccess{};
1115  }
1116  return as<Index>();
1117  }
1118 
1134  template <std::size_t Index>
1135  [[nodiscard]] constexpr variant_alternative_t<Index, Variant>&& get() && {
1136  if (!is<Index>()) {
1137  throw BadVariantAccess{};
1138  }
1139  return std::move(as<Index>());
1140  }
1141 
1157  template <std::size_t Index>
1158  [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>&& get() const&& {
1159  if (!is<Index>()) {
1160  throw BadVariantAccess{};
1161  }
1162  return std::move(as<Index>());
1163  }
1164 
1178  template <typename T>
1179  [[nodiscard]] constexpr T* get_if() noexcept requires(variant_has_alternative_v<T, Variant>) {
1180  return (is<T>()) ? &as<T>() : nullptr;
1181  }
1182 
1197  template <typename T>
1198  [[nodiscard]] constexpr const T* get_if() const noexcept requires(variant_has_alternative_v<T, Variant>) {
1199  return (is<T>()) ? &as<T>() : nullptr;
1200  }
1201 
1215  template <std::size_t Index>
1216  [[nodiscard]] constexpr variant_alternative_t<Index, Variant>* get_if() noexcept {
1217  return (is<Index>()) ? &as<Index>() : nullptr;
1218  }
1219 
1234  template <std::size_t Index>
1235  [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>* get_if() const noexcept {
1236  return (is<Index>()) ? &as<Index>() : nullptr;
1237  }
1238 
1256  template <typename Visitor, typename V>
1257  static constexpr decltype(auto) visit(Visitor&& visitor, V&& variant) { // NOLINT(cppcoreguidelines-missing-std-forward)
1258  constexpr bool IS_ALL_LVALUE_REFERENCE =
1259  (std::is_lvalue_reference_v<decltype(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Ts>()))> && ...);
1260  constexpr bool IS_ANY_CONST =
1261  (std::is_const_v<std::remove_reference_t<decltype(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Ts>()))>> || ...);
1262  using R = std::common_type_t<decltype(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Ts>()))...>;
1263  if constexpr (std::is_void_v<R>) {
1264  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
1265  if (!(((variant.template is<Indices>()) ? (std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Indices>()), true) : false) || ...)) {
1266  throw BadVariantAccess{};
1267  }
1268  }(std::make_index_sequence<sizeof...(Ts)>{});
1269  } else if constexpr (IS_ALL_LVALUE_REFERENCE) {
1270  std::conditional_t<IS_ANY_CONST, const R*, R*> result = nullptr;
1271  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
1272  if (!(((variant.template is<Indices>()) ? ((result = &std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Indices>())), true)
1273  : false) ||
1274  ...)) {
1275  throw BadVariantAccess{};
1276  }
1277  }(std::make_index_sequence<sizeof...(Ts)>{});
1278  return *result;
1279  } else {
1280  std::optional<R> result{};
1281  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
1282  if (!(((variant.template is<Indices>()) ? (result.emplace(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Indices>())), true)
1283  : false) ||
1284  ...)) {
1285  throw BadVariantAccess{};
1286  }
1287  }(std::make_index_sequence<sizeof...(Ts)>{});
1288  return R{std::move(*result)};
1289  }
1290  }
1291 
1292 private:
1293  void destroy() noexcept {
1294  if constexpr (!HAS_TRIVIAL_DESTRUCTOR) {
1295  storage.destroy(activeTypeIndex);
1296  }
1297  activeTypeIndex = npos;
1298  }
1299 
1300  detail::UnionStorage<Ts..., Monostate> storage;
1301  index_type activeTypeIndex;
1302 };
1303 
1321 template <typename Visitor, detail::derived_from_template_specialization_of<Variant> V>
1322 constexpr decltype(auto) visit(Visitor&& visitor, V&& variant) {
1323  return std::remove_cvref_t<V>::visit(std::forward<Visitor>(visitor), std::forward<V>(variant));
1324 }
1325 
1339 template <typename T, typename... Ts>
1340 [[nodiscard]] constexpr bool holds_alternative(const Variant<Ts...>& variant) noexcept {
1341  return variant.template is<T>();
1342 }
1343 
1357 template <std::size_t Index, typename... Ts>
1358 [[nodiscard]] constexpr bool holds_alternative(const Variant<Ts...>& variant) noexcept {
1359  return variant.template is<Index>();
1360 }
1361 
1377 template <typename T, typename... Ts>
1378 [[nodiscard]] constexpr T& get(Variant<Ts...>& variant) {
1379  return variant.template get<T>();
1380 }
1381 
1397 template <typename T, typename... Ts>
1398 [[nodiscard]] constexpr const T& get(const Variant<Ts...>& variant) {
1399  return variant.template get<T>();
1400 }
1401 
1417 template <std::size_t Index, typename... Ts>
1418 [[nodiscard]] constexpr variant_alternative_t<Index, Variant<Ts...>>& get(Variant<Ts...>& variant) {
1419  return variant.template get<Index>();
1420 }
1421 
1437 template <std::size_t Index, typename... Ts>
1438 [[nodiscard]] constexpr const variant_alternative_t<Index, Variant<Ts...>>& get(const Variant<Ts...>& variant) {
1439  return variant.template get<Index>();
1440 }
1441 
1457 template <typename T, typename... Ts>
1458 [[nodiscard]] constexpr T* get_if(Variant<Ts...>* variant) noexcept {
1459  assert(variant);
1460  return variant->template get_if<T>();
1461 }
1462 
1479 template <typename T, typename... Ts>
1480 [[nodiscard]] constexpr const T* get_if(const Variant<Ts...>* variant) noexcept {
1481  assert(variant);
1482  return variant->template get_if<T>();
1483 }
1484 
1500 template <std::size_t Index, typename... Ts>
1501 [[nodiscard]] constexpr variant_alternative_t<Index, Variant<Ts...>>* get_if(Variant<Ts...>* variant) noexcept {
1502  assert(variant);
1503  return variant->template get_if<Index>();
1504 }
1505 
1521 template <std::size_t Index, typename... Ts>
1522 [[nodiscard]] constexpr const variant_alternative_t<Index, Variant<Ts...>>* get_if(const Variant<Ts...>* variant) noexcept {
1523  assert(variant);
1524  return variant->template get_if<Index>();
1525 }
1526 
1539 template <typename... Ts>
1540 [[nodiscard]] constexpr bool operator==(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1541  if (a.index() != b.index()) {
1542  return false;
1543  }
1544  if (a.valueless_by_exception()) {
1545  return true;
1546  }
1547  return visit([&]<typename T>(const T& value) -> bool { return value == b.template as<T>(); }, a);
1548 }
1549 
1562 template <typename... Ts>
1563 [[nodiscard]] constexpr bool operator!=(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1564  if (a.index() != b.index()) {
1565  return true;
1566  }
1567  if (a.valueless_by_exception()) {
1568  return false;
1569  }
1570  return visit([&]<typename T>(const T& value) -> bool { return value != b.template as<T>(); }, a);
1571 }
1572 
1585 template <typename... Ts>
1586 [[nodiscard]] constexpr bool operator<(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1588  return false;
1589  }
1590  if (a.valueless_by_exception()) {
1591  return true;
1592  }
1593  if (b.valueless_by_exception()) {
1594  return false;
1595  }
1596  if (a.index() != b.index()) {
1597  return a.index() < b.index();
1598  }
1599  return visit([&]<typename T>(const T& value) -> bool { return value < b.template as<T>(); }, a);
1600 }
1601 
1614 template <typename... Ts>
1615 [[nodiscard]] constexpr bool operator<=(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1617  return true;
1618  }
1619  if (a.valueless_by_exception()) {
1620  return true;
1621  }
1622  if (b.valueless_by_exception()) {
1623  return false;
1624  }
1625  if (a.index() != b.index()) {
1626  return a.index() <= b.index();
1627  }
1628  return visit([&]<typename T>(const T& value) -> bool { return value <= b.template as<T>(); }, a);
1629 }
1630 
1643 template <typename... Ts>
1644 [[nodiscard]] constexpr bool operator>(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1646  return false;
1647  }
1648  if (a.valueless_by_exception()) {
1649  return false;
1650  }
1651  if (b.valueless_by_exception()) {
1652  return true;
1653  }
1654  if (a.index() != b.index()) {
1655  return a.index() > b.index();
1656  }
1657  return visit([&]<typename T>(const T& value) -> bool { return value > b.template as<T>(); }, a);
1658 }
1659 
1672 template <typename... Ts>
1673 [[nodiscard]] constexpr bool operator>=(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1675  return true;
1676  }
1677  if (a.valueless_by_exception()) {
1678  return false;
1679  }
1680  if (b.valueless_by_exception()) {
1681  return true;
1682  }
1683  if (a.index() != b.index()) {
1684  return a.index() >= b.index();
1685  }
1686  return visit([&]<typename T>(const T& value) -> bool { return value >= b.template as<T>(); }, a);
1687 }
1688 
1701 template <typename... Ts>
1702 [[nodiscard]] constexpr std::common_comparison_category_t<std::compare_three_way_result_t<Ts>...> operator<=>(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1703  if (a.valueless_by_exception() && b.valueless_by_exception()) {
1704  return std::strong_ordering::equal;
1705  }
1706  if (a.valueless_by_exception()) {
1707  return std::strong_ordering::less;
1708  }
1709  if (b.valueless_by_exception()) {
1710  return std::strong_ordering::greater;
1711  }
1712  if (a.index() != b.index()) {
1713  return a.index() <=> b.index();
1714  }
1715  return visit([&]<typename T>(const T& value) -> std::common_comparison_category_t<std::compare_three_way_result_t<Ts>...> { return value <=> b.template as<T>(); }, a);
1716 }
1717 
1736 template <typename V>
1737 [[nodiscard]] constexpr detail::Matcher<V> match(V&& variant) {
1738  return detail::Matcher<V>{std::forward<V>(variant)};
1739 }
1740 
1741 } // namespace donut
1742 
1746 template <typename... Ts>
1747 class std::hash<donut::Variant<Ts...>> {
1748 public:
1749  [[nodiscard]] std::size_t operator()(const donut::Variant<Ts...>& variant) const {
1750  return visit(hasher, variant);
1751  }
1752 
1753 private:
1754  template <typename T>
1755  class Hash {
1756  public:
1757  [[nodiscard]] std::size_t operator()(const T& value) const {
1758  return hasher(value);
1759  }
1760 
1761  private:
1762  [[no_unique_address]] std::hash<T> hasher{};
1763  };
1764 
1765  class HashVisitor : public Hash<Ts>... {
1766  public:
1767  using Hash<Ts>::operator()...;
1768  };
1769 
1770  [[no_unique_address]] HashVisitor hasher{};
1771 };
1772 
1773 #endif
Tagged union value type that holds a value of one of the given types.
Definition: Variant.hpp:291
constexpr Variant(std::in_place_index_t< Index > index, Args &&... args) requires(Index< sizeof...(Ts) &&std
Construct a variant alternative in-place given its index.
Definition: Variant.hpp:419
constexpr index_type index() const noexcept
Get the alternative index of the currently active value held by the variant.
Definition: Variant.hpp:696
constexpr variant_alternative_t< Index, Variant > & emplace(std::initializer_list< U > ilist, Args &&... args) requires(std
Construct an alternative given its index, with an initializer list as the first constructor argument,...
Definition: Variant.hpp:637
constexpr Variant(std::in_place_type_t< T > type, std::initializer_list< U > ilist, Args &&... args) requires(variant_has_alternative_v< T
Construct a variant alternative in-place given its type, with an initializer list as the first constr...
constexpr T & emplace(Args &&... args) requires(variant_has_alternative_v< T
Construct an alternative given its type, destroying the old value.
constexpr ~Variant()
Destructor.
Definition: Variant.hpp:446
constexpr Variant(Variant &&other) noexcept((std::is_nothrow_move_constructible_v< Ts > &&...)) requires(HAS_MOVE_CONSTRUCTOR &&!HAS_TRIVIAL_MOVE_CONSTRUCTOR)
Move constructor.
Definition: Variant.hpp:468
constexpr variant_alternative_t< Index, Variant > && get() &&
Access the underlying value with the given index.
Definition: Variant.hpp:1135
constexpr T & emplace(std::initializer_list< U > ilist, Args &&... args) requires(variant_has_alternative_v< T
Construct an alternative given its type, with an initializer list as the first constructor argument,...
constexpr const variant_alternative_t< Index, Variant > && as() const &&noexcept
Access the underlying value with the given index without a safety check.
Definition: Variant.hpp:978
constexpr Variant & operator=(Variant &&other) noexcept requires(HAS_MOVE_ASSIGNMENT &&HAS_TRIVIAL_MOVE_ASSIGNMENT)=default
Trivial move assignment.
constexpr const T && as() const &&noexcept requires(variant_has_alternative_v< T
Access the underlying value with the given type without a safety check.
constexpr T && as() &&noexcept requires(variant_has_alternative_v< T
Access the underlying value with the given type without a safety check.
constexpr Variant ilist
Definition: Variant.hpp:403
constexpr Variant(Variant &&other) noexcept requires(HAS_MOVE_CONSTRUCTOR &&HAS_TRIVIAL_MOVE_CONSTRUCTOR)=default
Trivial move constructor.
constexpr void swap(Variant &other) noexcept(((std::is_nothrow_move_constructible_v< Ts > &&std::is_nothrow_swappable_v< Ts >)&&...))
Swap this variant's value with that of another.
Definition: Variant.hpp:658
constexpr Variant(std::in_place_type_t< T > type, Args &&... args) requires(variant_has_alternative_v< T
Construct a variant alternative in-place given its type.
constexpr const variant_alternative_t< Index, Variant > & as() const &noexcept
Access the underlying value with the given index without a safety check.
Definition: Variant.hpp:916
constexpr Variant & operator=(const Variant &other) requires(HAS_COPY_ASSIGNMENT &&!HAS_TRIVIAL_COPY_ASSIGNMENT)
Copy assignment.
Definition: Variant.hpp:486
constexpr T && get() &&requires(variant_has_alternative_v< T
Access the underlying value with the given type.
constexpr const T & as() const &noexcept requires(variant_has_alternative_v< T
Access the underlying value with the given type without a safety check.
constexpr Variant & operator=(const Variant &other) requires(HAS_COPY_ASSIGNMENT &&HAS_TRIVIAL_COPY_ASSIGNMENT)=default
Trivial copy assignment.
constexpr bool is() const noexcept
Check if the variant currently holds the alternative with the given index.
Definition: Variant.hpp:735
constexpr bool is() const noexcept requires(variant_has_alternative_v< T
Check if the variant currently holds the alternative with the given type.
constexpr Variant & operator=(Variant &&other) noexcept(((std::is_nothrow_move_constructible_v< Ts > &&//NOLINT(performance-noexcept-move-constructor, cppcoreguidelines-noexcept-move-operations) std::is_nothrow_move_assignable_v< Ts >)&&...)) requires(HAS_MOVE_ASSIGNMENT &&!HAS_TRIVIAL_MOVE_ASSIGNMENT)
Move assignment.
Definition: Variant.hpp:500
constexpr const variant_alternative_t< Index, Variant > * get_if() const noexcept
Access the underlying value with the given index if it is the currently active alternative.
Definition: Variant.hpp:1235
constexpr variant_alternative_t< Index, Variant > & get() &
Access the underlying value with the given index.
Definition: Variant.hpp:1089
constexpr const T & get() const &requires(variant_has_alternative_v< T
Access the underlying value with the given type.
constexpr Variant & operator=(U &&value) noexcept(std::is_nothrow_assignable_v< decltype(F(std::forward< U >(value)))&, U > &&std::is_nothrow_constructible_v< decltype(F(std::forward< U >(value))), U >) requires(!std
Converting assignment.
Definition: Variant.hpp:535
constexpr const variant_alternative_t< Index, Variant > && get() const &&
Access the underlying value with the given index.
Definition: Variant.hpp:1158
constexpr Variant(std::in_place_index_t< Index > index, std::initializer_list< U > ilist, Args &&... args) requires(Index< sizeof...(Ts) &&std
Construct a variant alternative in-place given its index, with an initializer list as the first const...
Definition: Variant.hpp:440
constexpr T & get() &requires(variant_has_alternative_v< T
Access the underlying value with the given type.
constexpr variant_alternative_t< Index, Variant > && as() &&noexcept
Access the underlying value with the given index without a safety check.
Definition: Variant.hpp:947
std::conditional_t< sizeof...(Ts)< 255ull, std::uint8_t, std::conditional_t< sizeof...(Ts)< 65535ull, std::uint16_t, std::conditional_t< sizeof...(Ts)< 4294967295ull, std::uint32_t, std::uint64_t > >> index_type
Index type used to encode the active alternative type.
Definition: Variant.hpp:332
constexpr variant_alternative_t< Index, Variant > & as() &noexcept
Access the underlying value with the given index without a safety check.
Definition: Variant.hpp:885
constexpr T * get_if() noexcept requires(variant_has_alternative_v< T
Access the underlying value with the given type if it is the currently active alternative.
constexpr friend void swap(Variant &a, Variant &b) noexcept(noexcept(a.swap(b)))
Swap the values of two variants.
Definition: Variant.hpp:685
constexpr T & as() &noexcept requires(variant_has_alternative_v< T
Access the underlying value with the given type without a safety check.
constexpr variant_alternative_t< Index, Variant > * get_if() noexcept
Access the underlying value with the given index if it is the currently active alternative.
Definition: Variant.hpp:1216
constexpr Variant & operator=(const Variant &other) requires(!HAS_COPY_ASSIGNMENT)=delete
Deleted copy assignment.
constexpr bool valueless_by_exception() const noexcept
Check if the variant is in the valueless by exception state.
Definition: Variant.hpp:706
constexpr Variant(U &&value) noexcept(std::is_nothrow_constructible_v< decltype(F(std::forward< U >(value)))>) requires(!std
Converting constructor.
Definition: Variant.hpp:363
static constexpr index_type npos
Invalid alternative index, representing the valueless by exception state.
Definition: Variant.hpp:338
return std::move(as< variant_index_v< T, Variant >>())
static constexpr decltype(auto) visit(Visitor &&visitor, V &&variant)
Call a visitor functor with the currently active underlying value of a variant.
Definition: Variant.hpp:1257
constexpr ~Variant() requires(HAS_TRIVIAL_DESTRUCTOR)=default
Trivial destructor.
constexpr variant_alternative_t< Index, Variant > & emplace(Args &&... args) requires(std
Construct an alternative given its index, destroying the old value.
Definition: Variant.hpp:610
constexpr const T * get_if() const noexcept requires(variant_has_alternative_v< T
Access the underlying value with the given type if it is the currently active alternative.
constexpr const variant_alternative_t< Index, Variant > & get() const &
Access the underlying value with the given index.
Definition: Variant.hpp:1112
constexpr Variant() noexcept(std::is_nothrow_default_constructible_v< variant_alternative_t< 0, Variant >>) requires(HAS_DEFAULT_CONSTRUCTOR)
Default-construct a variant with the first variant alternative, if it is default-constructible.
Definition: Variant.hpp:347
constexpr const T && get() const &&requires(variant_has_alternative_v< T
Access the underlying value with the given type.
std::size_t operator()(const donut::Monostate &) const
Definition: Variant.hpp:270
std::size_t operator()(const donut::Variant< Ts... > &variant) const
Definition: Variant.hpp:1749
void swap(Object &a, Object &b) noexcept
Definition: json.hpp:3980
Definition: Application.hpp:9
constexpr decltype(auto) visit(Visitor &&visitor, V &&variant)
Call a visitor functor with the currently active underlying value of a variant.
Definition: Variant.hpp:1322
constexpr bool operator!=(const Variant< Ts... > &a, const Variant< Ts... > &b)
Compare two variants for inequality.
Definition: Variant.hpp:1563
Overloaded(Functors...) -> Overloaded< Functors... >
constexpr T * get_if(Variant< Ts... > *variant) noexcept
Access the underlying value with the given type of a variant if it is the currently active alternativ...
Definition: Variant.hpp:1458
constexpr bool holds_alternative(const Variant< Ts... > &variant) noexcept
Check if a variant currently holds the alternative with the given type.
Definition: Variant.hpp:1340
constexpr bool operator==(Monostate, Monostate) noexcept
Compare two monostates for equality.
Definition: Variant.hpp:246
constexpr std::size_t variant_index_v
Get the index of the variant alternative with a given type in a variant type.
Definition: Variant.hpp:190
constexpr bool operator<=(const Variant< Ts... > &a, const Variant< Ts... > &b)
Check if a variant is less than or equal to another variant.
Definition: Variant.hpp:1615
constexpr std::size_t variant_size_v
Get the number of alternative types of a variant type.
Definition: Variant.hpp:228
constexpr bool operator>=(const Variant< Ts... > &a, const Variant< Ts... > &b)
Check if a variant is greater than or equal to another variant.
Definition: Variant.hpp:1673
constexpr bool variant_has_alternative_v
Check if a variant type has a given type as one of its possible alternatives.
Definition: Variant.hpp:173
constexpr T & get(Variant< Ts... > &variant)
Access the underlying value with the given type of a variant.
Definition: Variant.hpp:1378
constexpr bool operator<(const Variant< Ts... > &a, const Variant< Ts... > &b)
Check if a variant is less than another variant.
Definition: Variant.hpp:1586
constexpr bool operator>(const Variant< Ts... > &a, const Variant< Ts... > &b)
Check if a variant is greater than another variant.
Definition: Variant.hpp:1644
constexpr detail::Matcher< V > match(V &&variant)
Choose a function overload to execute based on the active alternative of a variant.
Definition: Variant.hpp:1737
typename variant_alternative< Index, V >::type variant_alternative_t
Get the type of the variant alternative with a given index in a variant type.
Definition: Variant.hpp:212
constexpr std::strong_ordering operator<=>(Monostate, Monostate) noexcept
Compare two monostates.
Definition: Variant.hpp:258
Exception type that is thrown on an attempt to erroneously access an inactive alternative of a Varian...
Definition: Variant.hpp:282
BadVariantAccess() noexcept=default
const char * what() const noexcept override
Definition: Variant.hpp:285
Unit type for representing an empty alternative in Variant.
Definition: Variant.hpp:236