libdonut 2.3.6
Application framework for cross-platform game development in C++20
Loading...
Searching...
No Matches
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 <optional> // std::optional
13#include <type_traits> // std::is_..._v, std::false_type, std::true_type, std::integral_constant, std::remove_..._t, std::common_type_t
14#include <utility> // std::move, std::forward, std::swap, std::in_place_..., std::...index_sequence
15
16namespace donut {
17
26template <typename... Ts>
27class Variant;
28
29namespace detail {
30
31template <auto...>
32struct ConstantList {};
33
34template <std::size_t N, typename Union, auto... MemberPointers>
35struct get_n_member_pointers {
36 using type = ConstantList<MemberPointers..., &Union::head>;
37};
38
39template <std::size_t N, typename Union, auto... MemberPointers>
40requires(N > 0) struct get_n_member_pointers<N, Union, MemberPointers...> : get_n_member_pointers<N - 1, typename Union::Tail, MemberPointers..., &Union::tail> {};
41
42template <std::size_t N, typename Union>
43using get_n_member_pointers_t = typename get_n_member_pointers<N, Union>::type;
44
45template <auto... MemberPointers, typename U>
46[[nodiscard]] constexpr auto& getUnionMemberImpl(ConstantList<MemberPointers...>, U&& u) noexcept {
47 return (std::forward<U>(u).*....*MemberPointers);
48}
49
50template <std::size_t Index, typename U>
51[[nodiscard]] constexpr auto& getUnionMember(U& u) noexcept {
52 return getUnionMemberImpl(get_n_member_pointers_t<Index, U>{}, u);
53}
54
55template <typename... Ts>
56union UnionStorage {
57 UnionStorage() = default;
58
59 template <std::size_t Index, typename... Args>
60 UnionStorage(std::in_place_index_t<Index>, Args&&...) = delete; // NOLINT(cppcoreguidelines-missing-std-forward)
61};
62
63template <typename First, typename... Rest>
64union UnionStorage<First, Rest...> {
65 static constexpr std::size_t size = 1 + sizeof...(Rest);
66
67 using Head = First;
68 using Tail = UnionStorage<Rest...>;
69
70 constexpr UnionStorage()
71 : tail() {}
72
73 template <typename... Args>
74 constexpr UnionStorage(std::in_place_index_t<0>, Args&&... args)
75 : head(std::forward<Args>(args)...) {}
76
77 template <std::size_t Index, typename... Args>
78 constexpr UnionStorage(std::in_place_index_t<Index>, Args&&... args)
79 : tail(std::in_place_index<Index - 1>, std::forward<Args>(args)...) {}
80
81 constexpr UnionStorage(const UnionStorage& other, std::size_t other_index) {
82 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
83 (void)(((other_index == Indices) ? (create<Indices>(getUnionMember<Indices>(other)), true) : false) || ...);
84 }(std::make_index_sequence<size>{});
85 }
86
87 constexpr UnionStorage(UnionStorage&& other, std::size_t other_index) { // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
88 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
89 (void)(((other_index == Indices) ? (create<Indices>(std::move(getUnionMember<Indices>(other))), true) : false) || ...);
90 }(std::make_index_sequence<size>{});
91 }
92
93 ~UnionStorage() = default;
94 constexpr ~UnionStorage() requires(!std::is_trivially_destructible_v<Head> || !std::is_trivially_destructible_v<Tail>) {}
95
96 UnionStorage(const UnionStorage&) = default;
97 UnionStorage(UnionStorage&&) = default;
98 UnionStorage& operator=(const UnionStorage&) = default;
99 UnionStorage& operator=(UnionStorage&&) = default;
100
101 template <std::size_t Index, typename... Args>
102 constexpr void create(Args&&... args) {
103 std::construct_at(&getUnionMember<Index>(*this), std::forward<Args>(args)...);
104 }
105
106 constexpr void destroy(std::size_t index) noexcept {
107 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
108 (void)(((index == Indices) ? (std::destroy_at(&getUnionMember<Indices>(*this)), true) : false) || ...);
109 }(std::make_index_sequence<size>{});
110 }
111
112 Head head;
113 Tail tail;
114};
115
116template <template <typename...> typename Template, typename... TemplateArgs>
117constexpr void derivedFromTemplateSpecializationTest(const Template<TemplateArgs...>&);
118
119template <typename T, template <typename...> typename Template>
120concept derived_from_template_specialization_of = requires(const T& t) { derivedFromTemplateSpecializationTest<Template>(t); };
121
122template <typename T, std::size_t Index, typename... Ts>
123struct VariantIndexImpl;
124
125template <typename T, std::size_t Index, typename First, typename... Rest>
126struct VariantIndexImpl<T, Index, First, Rest...> : VariantIndexImpl<T, Index + 1, Rest...> {};
127
128template <typename T, std::size_t Index, typename... Rest>
129struct VariantIndexImpl<T, Index, T, Rest...> : std::integral_constant<std::size_t, Index> {};
130
131template <typename... Functors>
132struct Overloaded : Functors... {
133 using Functors::operator()...;
134};
135
136template <typename... Functors>
137Overloaded(Functors...) -> Overloaded<Functors...>;
138
139template <typename V>
140struct Matcher {
141 V variant;
142
143 template <typename... Functors>
144 constexpr decltype(auto) operator()(Functors&&... functors) const {
145 return visit(detail::Overloaded{std::forward<Functors>(functors)...}, variant);
146 }
147};
148
149} // namespace detail
150
152template <typename T, typename V>
153struct variant_has_alternative;
154
155template <typename T, typename First, typename... Rest>
156struct variant_has_alternative<T, Variant<First, Rest...>> : variant_has_alternative<T, Variant<Rest...>> {};
157
158template <typename T>
159struct variant_has_alternative<T, Variant<>> : std::false_type {};
160
161template <typename T, typename... Rest>
162struct variant_has_alternative<T, Variant<T, Rest...>> : std::true_type {};
164
171template <typename T, typename V>
172inline constexpr bool variant_has_alternative_v = variant_has_alternative<T, V>::value;
173
175template <typename T, typename V>
176struct variant_index;
177
178template <typename T, typename... Ts>
179struct variant_index<T, Variant<Ts...>> : detail::VariantIndexImpl<T, 0, Ts...> {};
181
188template <typename T, typename V>
189inline constexpr std::size_t variant_index_v = variant_index<T, V>::value;
190
192template <std::size_t Index, typename V>
193struct variant_alternative;
194
195template <std::size_t Index, typename First, typename... Rest>
196struct variant_alternative<Index, Variant<First, Rest...>> : variant_alternative<Index - 1, Variant<Rest...>> {};
197
198template <typename T, typename... Rest>
199struct variant_alternative<0, Variant<T, Rest...>> {
200 using type = T;
201};
203
210template <std::size_t Index, typename V>
211using variant_alternative_t = typename variant_alternative<Index, V>::type;
212
214template <typename V>
215struct variant_size;
216
217template <typename... Ts>
218struct variant_size<Variant<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};
220
226template <typename V>
227inline constexpr std::size_t variant_size_v = variant_size<V>::value;
228
235struct Monostate {};
236
245constexpr bool operator==(Monostate, Monostate) noexcept {
246 return true;
247}
248
257constexpr std::strong_ordering operator<=>(Monostate, Monostate) noexcept {
258 return std::strong_ordering::equal;
259}
260
261} // namespace donut
262
266template <>
267class std::hash<donut::Monostate> {
268public:
269 [[nodiscard]] std::size_t operator()(const donut::Monostate&) const {
270 return 0;
271 }
272};
273
274namespace donut {
275
281struct BadVariantAccess : std::exception {
282 BadVariantAccess() noexcept = default;
283
284 [[nodiscard]] const char* what() const noexcept override {
285 return "Bad variant access.";
286 }
287};
288
289template <typename... Ts>
290class Variant {
291private:
292 static constexpr bool HAS_DEFAULT_CONSTRUCTOR = std::is_default_constructible_v<variant_alternative_t<0, Variant>>;
293 static constexpr bool HAS_COPY_CONSTRUCTOR = (std::is_copy_constructible_v<Ts> && ...);
294 static constexpr bool HAS_MOVE_CONSTRUCTOR = (std::is_move_constructible_v<Ts> && ...);
295 static constexpr bool HAS_COPY_ASSIGNMENT = ((std::is_copy_constructible_v<Ts> && std::is_copy_assignable_v<Ts>) && ...);
296 static constexpr bool HAS_MOVE_ASSIGNMENT = ((std::is_move_constructible_v<Ts> && std::is_move_assignable_v<Ts>) && ...);
297 static constexpr bool HAS_TRIVIAL_COPY_CONSTRUCTOR = (std::is_trivially_copy_constructible_v<Ts> && ...);
298 static constexpr bool HAS_TRIVIAL_MOVE_CONSTRUCTOR = (std::is_trivially_move_constructible_v<Ts> && ...);
299 static constexpr bool HAS_TRIVIAL_COPY_ASSIGNMENT =
300 ((std::is_trivially_copy_constructible_v<Ts> && std::is_trivially_copy_assignable_v<Ts> && std::is_trivially_destructible_v<Ts>) && ...);
301 static constexpr bool HAS_TRIVIAL_MOVE_ASSIGNMENT =
302 ((std::is_trivially_move_constructible_v<Ts> && std::is_trivially_move_assignable_v<Ts> && std::is_trivially_destructible_v<Ts>) && ...);
303 static constexpr bool HAS_TRIVIAL_DESTRUCTOR = (std::is_trivially_destructible_v<Ts> && ...);
304
305 template <typename T>
306 struct SelectAlternativeTypeOverload {
307 template <typename U>
308 T operator()(T, U&& u) requires(requires { T{std::forward<U>(u)}; });
309 };
310
311 struct SelectAlternativeTypeOverloadSet : SelectAlternativeTypeOverload<Ts>... {
312 using SelectAlternativeTypeOverload<Ts>::operator()...;
313 };
314
315 template <typename U>
316 static constexpr auto F(U&& u) -> decltype(SelectAlternativeTypeOverloadSet{}(std::forward<U>(u), std::forward<U>(u)));
317
318public:
319 // clang-format off
328 std::conditional_t<sizeof...(Ts) < 255ull, std::uint8_t,
329 std::conditional_t<sizeof...(Ts) < 65535ull, std::uint16_t,
330 std::conditional_t<sizeof...(Ts) < 4294967295ull, std::uint32_t,
331 std::uint64_t>>>;
332 // clang-format on
333
337 static constexpr index_type npos = sizeof...(Ts);
338
346 constexpr Variant() noexcept(std::is_nothrow_default_constructible_v<variant_alternative_t<0, Variant>>) requires(HAS_DEFAULT_CONSTRUCTOR)
347 : Variant(std::in_place_index<0>) {}
348
361 template <typename U>
362 constexpr Variant(U&& value) noexcept(std::is_nothrow_constructible_v<decltype(F(std::forward<U>(value)))>)
363 requires(!std::is_same_v<std::remove_cvref_t<U>, Variant> && variant_has_alternative_v<decltype(F(std::forward<U>(value))), Variant> &&
364 std::is_constructible_v<decltype(F(std::forward<U>(value))), U>)
365 : Variant(std::in_place_index<variant_index_v<decltype(F(std::forward<U>(value))), Variant>>, std::forward<U>(value)) {}
366
378 template <typename T, typename... Args>
379 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...>)
380 : Variant(std::in_place_index<variant_index_v<T, Variant>>, std::forward<Args>(args)...) {
381 (void)type;
382 }
383
399 template <typename T, typename U, typename... Args>
400 constexpr explicit Variant(std::in_place_type_t<T> type, std::initializer_list<U> ilist, Args&&... args)
401 requires(variant_has_alternative_v<T, Variant> && std::is_constructible_v<T, std::initializer_list<U>&, Args...>)
402 : Variant(std::in_place_index<variant_index_v<T, Variant>>, ilist, std::forward<Args>(args)...) {
403 (void)type;
404 }
405
417 template <std::size_t Index, typename... Args>
418 constexpr explicit Variant(std::in_place_index_t<Index> index, Args&&... args)
419 requires(Index < sizeof...(Ts) && std::is_constructible_v<variant_alternative_t<Index, Variant>, Args...>)
420 : storage(index, std::forward<Args>(args)...)
421 , activeTypeIndex(Index) {}
422
438 template <std::size_t Index, typename U, typename... Args>
439 constexpr explicit Variant(std::in_place_index_t<Index> index, std::initializer_list<U> ilist, Args&&... args)
440 requires(Index < sizeof...(Ts) && std::is_constructible_v<variant_alternative_t<Index, Variant>, std::initializer_list<U>&, Args...>)
441 : storage(index, ilist, std::forward<Args>(args)...)
442 , activeTypeIndex(Index) {}
443
445 constexpr ~Variant() {
446 destroy();
447 }
448
450 constexpr ~Variant() requires(HAS_TRIVIAL_DESTRUCTOR) = default;
451
453 constexpr Variant(const Variant& other) requires(!HAS_COPY_CONSTRUCTOR) = delete;
454
456 constexpr Variant(const Variant& other) requires(HAS_COPY_CONSTRUCTOR && HAS_TRIVIAL_COPY_CONSTRUCTOR) = default;
457
459 constexpr Variant(const Variant& other) requires(HAS_COPY_CONSTRUCTOR && !HAS_TRIVIAL_COPY_CONSTRUCTOR)
460 : storage(other.storage, other.activeTypeIndex)
461 , activeTypeIndex(other.activeTypeIndex) {}
462
464 constexpr Variant(Variant&& other) noexcept requires(HAS_MOVE_CONSTRUCTOR && HAS_TRIVIAL_MOVE_CONSTRUCTOR) = default;
465
467 constexpr Variant(Variant&& other) noexcept(
468 (std::is_nothrow_move_constructible_v<Ts> && ...)) // NOLINT(performance-noexcept-move-constructor, cppcoreguidelines-noexcept-move-operations)
469 requires(HAS_MOVE_CONSTRUCTOR && !HAS_TRIVIAL_MOVE_CONSTRUCTOR)
470 : storage(std::move(other.storage), other.activeTypeIndex)
471 , activeTypeIndex(other.activeTypeIndex) {}
472
474 constexpr Variant& operator=(const Variant& other) requires(!HAS_COPY_ASSIGNMENT) = delete;
475
477 constexpr Variant& operator=(const Variant& other) requires(HAS_COPY_ASSIGNMENT && HAS_TRIVIAL_COPY_ASSIGNMENT) = default;
478
485 constexpr Variant& operator=(const Variant& other) requires(HAS_COPY_ASSIGNMENT && !HAS_TRIVIAL_COPY_ASSIGNMENT) {
486 *this = Variant{other};
487 return *this;
488 }
489
491 constexpr Variant& operator=(Variant&& other) noexcept requires(HAS_MOVE_ASSIGNMENT && HAS_TRIVIAL_MOVE_ASSIGNMENT) = default;
492
499 constexpr Variant& operator=(Variant&& other) noexcept(
500 ((std::is_nothrow_move_constructible_v<Ts> && // NOLINT(performance-noexcept-move-constructor, cppcoreguidelines-noexcept-move-operations)
501 std::is_nothrow_move_assignable_v<Ts>) &&
502 ...)) 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() & requires(variant_has_alternative_v<T, Variant>) {
999 if (!is<T>()) {
1000 throw BadVariantAccess{};
1001 }
1002 return as<T>();
1003 }
1004
1019 template <typename T>
1020 [[nodiscard]] constexpr const T& get() const& requires(variant_has_alternative_v<T, Variant>) {
1021 if (!is<T>()) {
1022 throw BadVariantAccess{};
1023 }
1024 return as<T>();
1025 }
1026
1041 template <typename T>
1042 [[nodiscard]] constexpr T&& get() &&
1043 requires(variant_has_alternative_v<T, Variant>) {
1044 if (!is<T>()) {
1045 throw BadVariantAccess{};
1046 }
1047 return std::move(as<T>());
1048 }
1049
1064 template <typename T>
1065 [[nodiscard]] constexpr const T&& get() const&& requires(variant_has_alternative_v<T, Variant>) {
1066 if (!is<T>()) {
1067 throw BadVariantAccess{};
1068 }
1069 return std::move(as<T>());
1070 }
1071
1087 template <std::size_t Index>
1088 [[nodiscard]] constexpr variant_alternative_t<Index, Variant>& get() & {
1089 if (!is<Index>()) {
1090 throw BadVariantAccess{};
1091 }
1092 return as<Index>();
1093 }
1094
1110 template <std::size_t Index>
1111 [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>& get() const& {
1112 if (!is<Index>()) {
1113 throw BadVariantAccess{};
1114 }
1115 return as<Index>();
1116 }
1117
1133 template <std::size_t Index>
1134 [[nodiscard]] constexpr variant_alternative_t<Index, Variant>&& get() && {
1135 if (!is<Index>()) {
1136 throw BadVariantAccess{};
1137 }
1138 return std::move(as<Index>());
1139 }
1140
1156 template <std::size_t Index>
1157 [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>&& get() const&& {
1158 if (!is<Index>()) {
1159 throw BadVariantAccess{};
1160 }
1161 return std::move(as<Index>());
1162 }
1163
1177 template <typename T>
1178 [[nodiscard]] constexpr T* get_if() noexcept requires(variant_has_alternative_v<T, Variant>) {
1179 return (is<T>()) ? &as<T>() : nullptr;
1180 }
1181
1196 template <typename T>
1197 [[nodiscard]] constexpr const T* get_if() const noexcept requires(variant_has_alternative_v<T, Variant>) {
1198 return (is<T>()) ? &as<T>() : nullptr;
1199 }
1200
1214 template <std::size_t Index>
1215 [[nodiscard]] constexpr variant_alternative_t<Index, Variant>* get_if() noexcept {
1216 return (is<Index>()) ? &as<Index>() : nullptr;
1217 }
1218
1233 template <std::size_t Index>
1234 [[nodiscard]] constexpr const variant_alternative_t<Index, Variant>* get_if() const noexcept {
1235 return (is<Index>()) ? &as<Index>() : nullptr;
1236 }
1237
1255 template <typename Visitor, typename V>
1256 static constexpr decltype(auto) visit(Visitor&& visitor, V&& variant) { // NOLINT(cppcoreguidelines-missing-std-forward)
1257 constexpr bool IS_ALL_LVALUE_REFERENCE =
1258 (std::is_lvalue_reference_v<decltype(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Ts>()))> && ...);
1259 constexpr bool IS_ANY_CONST =
1260 (std::is_const_v<std::remove_reference_t<decltype(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Ts>()))>> || ...);
1261 using R = std::common_type_t<decltype(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Ts>()))...>;
1262 if constexpr (std::is_void_v<R>) {
1263 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
1264 if (!(((variant.template is<Indices>()) ? (std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Indices>()), true) : false) || ...)) {
1265 throw BadVariantAccess{};
1266 }
1267 }(std::make_index_sequence<sizeof...(Ts)>{});
1268 } else if constexpr (IS_ALL_LVALUE_REFERENCE) {
1269 std::conditional_t<IS_ANY_CONST, const R*, R*> result = nullptr;
1270 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
1271 if (!(((variant.template is<Indices>()) ? ((result = &std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Indices>())), true)
1272 : false) ||
1273 ...)) {
1274 throw BadVariantAccess{};
1275 }
1276 }(std::make_index_sequence<sizeof...(Ts)>{});
1277 return *result;
1278 } else {
1279 std::optional<R> result{};
1280 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
1281 if (!(((variant.template is<Indices>()) ? (result.emplace(std::invoke(std::forward<Visitor>(visitor), std::forward<V>(variant).template as<Indices>())), true)
1282 : false) ||
1283 ...)) {
1284 throw BadVariantAccess{};
1285 }
1286 }(std::make_index_sequence<sizeof...(Ts)>{});
1287 return R{std::move(*result)};
1288 }
1289 }
1290
1291private:
1292 void destroy() noexcept {
1293 if constexpr (!HAS_TRIVIAL_DESTRUCTOR) {
1294 storage.destroy(activeTypeIndex);
1295 }
1296 activeTypeIndex = npos;
1297 }
1298
1299 detail::UnionStorage<Ts..., Monostate> storage;
1300 index_type activeTypeIndex;
1301};
1302
1320template <typename Visitor, detail::derived_from_template_specialization_of<Variant> V>
1321constexpr decltype(auto) visit(Visitor&& visitor, V&& variant) {
1322 return std::remove_cvref_t<V>::visit(std::forward<Visitor>(visitor), std::forward<V>(variant));
1323}
1324
1338template <typename T, typename... Ts>
1339[[nodiscard]] constexpr bool holds_alternative(const Variant<Ts...>& variant) noexcept {
1340 return variant.template is<T>();
1341}
1342
1356template <std::size_t Index, typename... Ts>
1357[[nodiscard]] constexpr bool holds_alternative(const Variant<Ts...>& variant) noexcept {
1358 return variant.template is<Index>();
1359}
1360
1376template <typename T, typename... Ts>
1377[[nodiscard]] constexpr T& get(Variant<Ts...>& variant) {
1378 return variant.template get<T>();
1379}
1380
1396template <typename T, typename... Ts>
1397[[nodiscard]] constexpr const T& get(const Variant<Ts...>& variant) {
1398 return variant.template get<T>();
1399}
1400
1416template <std::size_t Index, typename... Ts>
1417[[nodiscard]] constexpr variant_alternative_t<Index, Variant<Ts...>>& get(Variant<Ts...>& variant) {
1418 return variant.template get<Index>();
1419}
1420
1436template <std::size_t Index, typename... Ts>
1437[[nodiscard]] constexpr const variant_alternative_t<Index, Variant<Ts...>>& get(const Variant<Ts...>& variant) {
1438 return variant.template get<Index>();
1439}
1440
1456template <typename T, typename... Ts>
1457[[nodiscard]] constexpr T* get_if(Variant<Ts...>* variant) noexcept {
1458 assert(variant);
1459 return variant->template get_if<T>();
1460}
1461
1478template <typename T, typename... Ts>
1479[[nodiscard]] constexpr const T* get_if(const Variant<Ts...>* variant) noexcept {
1480 assert(variant);
1481 return variant->template get_if<T>();
1482}
1483
1499template <std::size_t Index, typename... Ts>
1500[[nodiscard]] constexpr variant_alternative_t<Index, Variant<Ts...>>* get_if(Variant<Ts...>* variant) noexcept {
1501 assert(variant);
1502 return variant->template get_if<Index>();
1503}
1504
1520template <std::size_t Index, typename... Ts>
1521[[nodiscard]] constexpr const variant_alternative_t<Index, Variant<Ts...>>* get_if(const Variant<Ts...>* variant) noexcept {
1522 assert(variant);
1523 return variant->template get_if<Index>();
1524}
1525
1538template <typename... Ts>
1539[[nodiscard]] constexpr bool operator==(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1540 if (a.index() != b.index()) {
1541 return false;
1542 }
1543 if (a.valueless_by_exception()) {
1544 return true;
1545 }
1546 return visit([&]<typename T>(const T& value) -> bool { return value == b.template as<T>(); }, a);
1547}
1548
1561template <typename... Ts>
1562[[nodiscard]] constexpr bool operator!=(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1563 if (a.index() != b.index()) {
1564 return true;
1565 }
1566 if (a.valueless_by_exception()) {
1567 return false;
1568 }
1569 return visit([&]<typename T>(const T& value) -> bool { return value != b.template as<T>(); }, a);
1570}
1571
1584template <typename... Ts>
1585[[nodiscard]] constexpr bool operator<(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1587 return false;
1588 }
1589 if (a.valueless_by_exception()) {
1590 return true;
1591 }
1592 if (b.valueless_by_exception()) {
1593 return false;
1594 }
1595 if (a.index() != b.index()) {
1596 return a.index() < b.index();
1597 }
1598 return visit([&]<typename T>(const T& value) -> bool { return value < b.template as<T>(); }, a);
1599}
1600
1613template <typename... Ts>
1614[[nodiscard]] constexpr bool operator<=(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1616 return true;
1617 }
1618 if (a.valueless_by_exception()) {
1619 return true;
1620 }
1621 if (b.valueless_by_exception()) {
1622 return false;
1623 }
1624 if (a.index() != b.index()) {
1625 return a.index() <= b.index();
1626 }
1627 return visit([&]<typename T>(const T& value) -> bool { return value <= b.template as<T>(); }, a);
1628}
1629
1642template <typename... Ts>
1643[[nodiscard]] constexpr bool operator>(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1645 return false;
1646 }
1647 if (a.valueless_by_exception()) {
1648 return false;
1649 }
1650 if (b.valueless_by_exception()) {
1651 return true;
1652 }
1653 if (a.index() != b.index()) {
1654 return a.index() > b.index();
1655 }
1656 return visit([&]<typename T>(const T& value) -> bool { return value > b.template as<T>(); }, a);
1657}
1658
1671template <typename... Ts>
1672[[nodiscard]] constexpr bool operator>=(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1674 return true;
1675 }
1676 if (a.valueless_by_exception()) {
1677 return false;
1678 }
1679 if (b.valueless_by_exception()) {
1680 return true;
1681 }
1682 if (a.index() != b.index()) {
1683 return a.index() >= b.index();
1684 }
1685 return visit([&]<typename T>(const T& value) -> bool { return value >= b.template as<T>(); }, a);
1686}
1687
1700template <typename... Ts>
1701[[nodiscard]] constexpr std::common_comparison_category_t<std::compare_three_way_result_t<Ts>...> operator<=>(const Variant<Ts...>& a, const Variant<Ts...>& b) {
1703 return std::strong_ordering::equal;
1704 }
1705 if (a.valueless_by_exception()) {
1706 return std::strong_ordering::less;
1707 }
1708 if (b.valueless_by_exception()) {
1709 return std::strong_ordering::greater;
1710 }
1711 if (a.index() != b.index()) {
1712 return a.index() <=> b.index();
1713 }
1714 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);
1715}
1716
1735template <typename V>
1736[[nodiscard]] constexpr detail::Matcher<V> match(V&& variant) {
1737 return detail::Matcher<V>{std::forward<V>(variant)};
1738}
1739
1740} // namespace donut
1741
1745template <typename... Ts>
1746class std::hash<donut::Variant<Ts...>> {
1747public:
1748 [[nodiscard]] std::size_t operator()(const donut::Variant<Ts...>& variant) const {
1749 return visit(hasher, variant);
1750 }
1751
1752private:
1753 template <typename T>
1754 class Hash {
1755 public:
1756 [[nodiscard]] std::size_t operator()(const T& value) const {
1757 return hasher(value);
1758 }
1759
1760 private:
1761 [[no_unique_address]] std::hash<T> hasher{};
1762 };
1763
1764 class HashVisitor : public Hash<Ts>... {
1765 public:
1766 using Hash<Ts>::operator()...;
1767 };
1768
1769 [[no_unique_address]] HashVisitor hasher{};
1770};
1771
1772#endif
Tagged union value type that holds a value of one of the given types.
Definition Variant.hpp:290
constexpr Variant(std::in_place_index_t< Index > index, Args &&... args)
Construct a variant alternative in-place given its index.
Definition Variant.hpp:418
constexpr variant_alternative_t< Index, Variant > && get() &&
Access the underlying value with the given index.
Definition Variant.hpp:1134
constexpr const T & as() const &noexcept
Access the underlying value with the given type without a safety check.
Definition Variant.hpp:794
constexpr index_type index() const noexcept
Get the alternative index of the currently active value held by the variant.
Definition Variant.hpp:696
constexpr T & as() &noexcept
Access the underlying value with the given type without a safety check.
Definition Variant.hpp:764
constexpr Variant(Variant &&other) noexcept((std::is_nothrow_move_constructible_v< Ts > &&...))
Move constructor.
Definition Variant.hpp:467
constexpr Variant(Variant &&other) noexcept=default
Trivial move constructor.
constexpr const T && as() const &&noexcept
Access the underlying value with the given type without a safety check.
Definition Variant.hpp:854
constexpr ~Variant()
Destructor.
Definition Variant.hpp:445
constexpr variant_alternative_t< Index, Variant > & emplace(Args &&... args)
Construct an alternative given its index, destroying the old value.
Definition Variant.hpp:610
constexpr T && get() &&
Access the underlying value with the given type.
Definition Variant.hpp:1042
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 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 >)
Converting assignment.
Definition Variant.hpp:535
constexpr Variant() noexcept(std::is_nothrow_default_constructible_v< variant_alternative_t< 0, Variant > >)
Default-construct a variant with the first variant alternative, if it is default-constructible.
Definition Variant.hpp:346
constexpr Variant(std::in_place_type_t< T > type, std::initializer_list< U > ilist, Args &&... args)
Construct a variant alternative in-place given its type, with an initializer list as the first constr...
Definition Variant.hpp:400
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(std::in_place_type_t< T > type, Args &&... args)
Construct a variant alternative in-place given its type.
Definition Variant.hpp:379
constexpr T * get_if() noexcept
Access the underlying value with the given type if it is the currently active alternative.
Definition Variant.hpp:1178
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 T && as() &&noexcept
Access the underlying value with the given type without a safety check.
Definition Variant.hpp:824
constexpr const variant_alternative_t< Index, Variant > && get() const &&
Access the underlying value with the given index.
Definition Variant.hpp:1157
constexpr const variant_alternative_t< Index, Variant > & get() const &
Access the underlying value with the given index.
Definition Variant.hpp:1111
constexpr variant_alternative_t< Index, Variant > && as() &&noexcept
Access the underlying value with the given index without a safety check.
Definition Variant.hpp:947
constexpr bool is() const noexcept
Check if the variant currently holds the alternative with the given index.
Definition Variant.hpp:735
constexpr Variant & operator=(const Variant &other)
Copy assignment.
Definition Variant.hpp:485
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(U &&value) noexcept(std::is_nothrow_constructible_v< decltype(F(std::forward< U >(value)))>)
Converting constructor.
Definition Variant.hpp:362
friend constexpr void swap(Variant &a, Variant &b) noexcept(noexcept(a.swap(b)))
Swap the values of two variants.
Definition Variant.hpp:685
constexpr const T * get_if() const noexcept
Access the underlying value with the given type if it is the currently active alternative.
Definition Variant.hpp:1197
constexpr Variant(const Variant &other)=default
Trivial copy constructor.
constexpr variant_alternative_t< Index, Variant > & get() &
Access the underlying value with the given index.
Definition Variant.hpp:1088
constexpr Variant & operator=(const Variant &other)=delete
Deleted copy assignment.
constexpr variant_alternative_t< Index, Variant > & emplace(std::initializer_list< U > ilist, Args &&... args)
Construct an alternative given its index, with an initializer list as the first constructor argument,...
Definition Variant.hpp:637
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:331
constexpr T & emplace(Args &&... args)
Construct an alternative given its type, destroying the old value.
Definition Variant.hpp:567
constexpr bool is() const noexcept
Check if the variant currently holds the alternative with the given type.
Definition Variant.hpp:720
constexpr const T & get() const &
Access the underlying value with the given type.
Definition Variant.hpp:1020
constexpr Variant(const Variant &other)=delete
Deleted copy constructor.
constexpr Variant(const Variant &other)
Copy constructor.
Definition Variant.hpp:459
constexpr bool valueless_by_exception() const noexcept
Check if the variant is in the valueless by exception state.
Definition Variant.hpp:706
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 >) &&...))
Move assignment.
Definition Variant.hpp:499
constexpr T & emplace(std::initializer_list< U > ilist, Args &&... args)
Construct an alternative given its type, with an initializer list as the first constructor argument,...
Definition Variant.hpp:590
static constexpr index_type npos
Invalid alternative index, representing the valueless by exception state.
Definition Variant.hpp:337
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:1256
constexpr const T && get() const &&
Access the underlying value with the given type.
Definition Variant.hpp:1065
constexpr Variant(std::in_place_index_t< Index > index, std::initializer_list< U > ilist, Args &&... args)
Construct a variant alternative in-place given its index, with an initializer list as the first const...
Definition Variant.hpp:439
constexpr Variant & operator=(Variant &&other) noexcept=default
Trivial move assignment.
constexpr Variant & operator=(const Variant &other)=default
Trivial copy assignment.
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:1234
constexpr T & get() &
Access the underlying value with the given type.
Definition Variant.hpp:998
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:1215
constexpr ~Variant()=default
Trivial destructor.
std::size_t operator()(const donut::Monostate &) const
Definition Variant.hpp:269
std::size_t operator()(const donut::Variant< Ts... > &variant) const
Definition Variant.hpp:1748
Definition Application.hpp:9
constexpr T & get(Variant< Ts... > &variant)
Access the underlying value with the given type of a variant.
Definition Variant.hpp:1377
constexpr decltype(auto) visit(Visitor &&visitor, V &&variant)
Call a visitor functor with the currently active underlying value of a variant.
Definition Variant.hpp:1321
constexpr bool operator!=(const Variant< Ts... > &a, const Variant< Ts... > &b)
Compare two variants for inequality.
Definition Variant.hpp:1562
Overloaded(Functors...) -> Overloaded< Functors... >
constexpr bool holds_alternative(const Variant< Ts... > &variant) noexcept
Check if a variant currently holds the alternative with the given type.
Definition Variant.hpp:1339
constexpr bool operator==(Monostate, Monostate) noexcept
Compare two monostates for equality.
Definition Variant.hpp:245
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:189
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:1614
constexpr detail::Matcher< V > match(V &&variant)
Choose a function overload to execute based on the active alternative of a variant.
Definition Variant.hpp:1736
constexpr std::size_t variant_size_v
Get the number of alternative types of a variant type.
Definition Variant.hpp:227
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:1672
constexpr bool variant_has_alternative_v
Check if a variant type has a given type as one of its possible alternatives.
Definition Variant.hpp:172
constexpr bool operator<(const Variant< Ts... > &a, const Variant< Ts... > &b)
Check if a variant is less than another variant.
Definition Variant.hpp:1585
constexpr bool operator>(const Variant< Ts... > &a, const Variant< Ts... > &b)
Check if a variant is greater than another variant.
Definition Variant.hpp:1643
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:1457
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:211
constexpr std::strong_ordering operator<=>(Monostate, Monostate) noexcept
Compare two monostates.
Definition Variant.hpp:257
Exception type that is thrown on an attempt to erroneously access an inactive alternative of a Varian...
Definition Variant.hpp:281
BadVariantAccess() noexcept=default
const char * what() const noexcept override
Definition Variant.hpp:284
Unit type for representing an empty alternative in Variant.
Definition Variant.hpp:235