plugify 1.2.6
Loading...
Searching...
No Matches
variant.hpp
1#pragma once
2
3#include <exception>
4#include <type_traits>
5#include <utility> // swap
6#include <limits> // used for index_type
7#include <initializer_list>
8#include <cassert>
9
10#ifndef PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
11# include <memory>
12#else
13# include <new>
14#endif
15
16#ifndef PLUGIFY_VARIANT_NO_STD_HASH
17# include <functional>
18#endif
19
20#define PLG_FWD(x) static_cast<decltype(x)&&>(x)
21#define PLG_MOV(x) static_cast< std::remove_reference_t<decltype(x)>&& >(x)
22
23#include "macro.hpp"
24
25// from https://github.com/groundswellaudio/swl-variant
26namespace plg {
27#if PLUGIFY_EXCEPTIONS
28 class bad_variant_access final : public std::exception {
29 const char* message = ""; // llvm test requires a well formed what() on default init
30 public :
31 explicit bad_variant_access(const char* str) noexcept : message{str} {}
32 bad_variant_access() noexcept = default;
33 bad_variant_access(const bad_variant_access&) noexcept = default;
34 bad_variant_access& operator=(const bad_variant_access&) noexcept = default;
35 const char* what() const noexcept override { return message; }
36 };
37#endif // PLUGIFY_EXCEPTIONS
38
39 namespace detail {
40 //struct variant_tag{};
41 struct emplacer_tag{};
42 }
43
44 template<class T>
46
47 template<std::size_t Index>
49
50 template<std::size_t Index>
51 inline static constexpr in_place_index_t<Index> in_place_index;
52
53 template<class T>
54 inline static constexpr in_place_type_t<T> in_place_type;
55
56 namespace detail {
57 template<int N>
58 constexpr int find_first_true(bool (&&arr)[N]) {
59 for (int k = 0; k < N; ++k)
60 if (arr[k])
61 return k;
62 return -1;
63 }
64
65 template<class T, class... Ts>
66 inline constexpr bool appears_exactly_once = (static_cast<unsigned short>(std::is_same_v<T, Ts>) + ...) == 1;
67
68 // ============= type pack element
69
70#if __has_builtin(__type_pack_element)
71 template<std::size_t K, class... Ts>
72 using type_pack_element = __type_pack_element<K, Ts...>;
73#else
74 template<unsigned char = 1>
76
77 template<>
78 struct find_type_i<1> {
79 template<std::size_t Idx, class T, class... Ts>
80 using f = typename find_type_i<(Idx != 1)>::template f<Idx - 1, Ts...>;
81 };
82
83 template<>
84 struct find_type_i<0> {
85 template<std::size_t, class T, class... Ts>
86 using f = T;
87 };
88
89 template<std::size_t K, class... Ts>
90 using type_pack_element = typename find_type_i<(K != 0)>::template f<K, Ts...>;
91#endif
92
93 // ============= overload match detector. to be used for variant generic assignment
94
95 template<class T>
96 using arr1 = T[1];
97
98 template<std::size_t N, class A>
100 using type = A;
101 template<class T>
102 requires requires { arr1<A>{std::declval<T>()}; }
103 auto operator()(A, T&&) -> overload_frag<N, A>;
104 };
105
106 template<class Seq, class... Args>
108
109 template<std::size_t... Idx, class... Args>
110 struct make_overload<std::integer_sequence<std::size_t, Idx...>, Args...>
111 : overload_frag<Idx, Args>... {
112 using overload_frag<Idx, Args>::operator()...;
113 };
114
115 template<class T, class... Ts>
116 using best_overload_match = typename decltype(
117 make_overload<std::make_index_sequence<sizeof...(Ts)>, Ts...>{}
118 (std::declval<T>(), std::declval<T>())
119 )::type;
120
121 template<class T, class... Ts>
123 requires { typename best_overload_match<T, Ts...>; };
124
125 // ================================== rel ops
126
127 template<class From, class To>
128 concept convertible = std::is_convertible_v<From, To>;
129
130 template<class T>
131 concept has_eq_comp = requires (T a, T b) {
132 { a == b } -> convertible<bool>;
133 };
134
135 template<class T>
136 concept has_lesser_comp = requires (T a, T b) {
137 { a < b } -> convertible<bool>;
138 };
139
140 template<class T>
141 concept has_less_or_eq_comp = requires (T a, T b) {
142 { a <= b } -> convertible<bool>;
143 };
144
145 template<class A>
147 template<class T>
148 constexpr void operator()(T&& elem, auto index_) const {
149 a.template emplace_no_dtor<index_>(static_cast<T&&>(elem));
150 }
151 A& a;
152 };
153
154 template<class E, class T>
155 constexpr void destruct(T& obj) {
156 if constexpr (not std::is_trivially_destructible_v<E>)
157 obj.~E();
158 }
159
160 // =============================== variant union types
161
162 // =================== base variant storage type
163 // this type is used to build a N-ary tree of union.
164
165 struct dummy_type{ static constexpr int elem_size = 0; }; // used to fill the back of union nodes
166
167 using union_index_t = unsigned;
168
169#define TRAIT(trait) (std::is_##trait##_v<A> && std::is_##trait##_v<B>)
170
171#define SFM(signature, trait) \
172 signature = default; \
173 signature requires (TRAIT(trait) and not TRAIT(trivially_##trait)) {};
174
175// given the two members of type A and B of an union X
176// this create the proper conditionally trivial special members functions
177#define INJECT_UNION_SFM(X) \
178 SFM(constexpr X (const X &), copy_constructible) \
179 SFM(constexpr X (X&&) noexcept, move_constructible) \
180 SFM(constexpr X& operator=(const X&), copy_assignable) \
181 SFM(constexpr X& operator=(X&&) noexcept, move_assignable) \
182 SFM(constexpr ~X(), destructible)
183
184 template<bool IsLeaf>
186
187 template<>
188 struct node_trait<true> {
189
190 template<class A, class B>
191 static constexpr auto elem_size = not(std::is_same_v<B, dummy_type>) ? 2 : 1;
192
193 template<std::size_t, class>
194 static constexpr char ctor_branch = 0;
195 };
196
197 template<>
199 template<class A, class B>
200 static constexpr auto elem_size = A::elem_size + B::elem_size;
201
202 template<std::size_t Index, class A>
203 static constexpr char ctor_branch = (Index < A::elem_size) ? 1 : 2;
204 };
205
206 template<bool IsLeaf, class A, class B>
207 struct union_node {
208 union {
209 A a;
210 B b;
211 };
212
213 static constexpr auto elem_size = node_trait<IsLeaf>::template elem_size<A, B>;
214
215 constexpr union_node() = default;
216
217 template<std::size_t Index, class... Args>
219 constexpr explicit union_node(in_place_index_t<Index>, Args&&... args)
220 : a{in_place_index<Index>, static_cast<Args&&>(args)...}
221 {}
222
223 template<std::size_t Index, class... Args>
225 constexpr explicit union_node(in_place_index_t<Index>, Args&&... args)
226 : b{in_place_index<Index - A::elem_size>, static_cast<Args&&>(args)...}
227 {}
228
229 template<class... Args>
230 requires (IsLeaf)
231 constexpr explicit union_node(in_place_index_t<0>, Args&&... args)
232 : a{static_cast<Args&&>(args)...}
233 {}
234
235 template<class... Args>
236 requires (IsLeaf)
237 constexpr explicit union_node(in_place_index_t<1>, Args&&... args)
238 : b{static_cast<Args&&>(args)...}
239 {}
240
241 constexpr explicit union_node(dummy_type)
242 requires (std::is_same_v<dummy_type, B>)
243 : b{}
244 {}
245
246 template<union_index_t Index>
247 constexpr auto& get() {
248 if constexpr (IsLeaf) {
249 if constexpr (Index == 0)
250 return a;
251 else
252 return b;
253 } else {
254 if constexpr (Index < A::elem_size)
255 return a.template get<Index>();
256 else
257 return b.template get<Index - A::elem_size>();
258 }
259 }
260
261 template<union_index_t Index>
262 constexpr const auto& get() const {
263 return const_cast<union_node&>(*this).get<Index>();
264 }
265
266 INJECT_UNION_SFM(union_node) //-V832
267 };
268
269#undef INJECT_UNION_SFM
270#undef SFM
271#undef TRAIT
272
273 // =================== algorithm to build the tree of unions
274 // take a sequence of types and perform an order preserving fold until only one type is left
275 // the first parameter is the numbers of types remaining for the current pass
276
277 constexpr unsigned char pick_next(unsigned remaining) {
278 return static_cast<unsigned char>(remaining >= 2 ? 2 : remaining);
279 }
280
281 template<unsigned char Pick, unsigned char GoOn, bool FirstPass>
282 struct make_tree;
283
284 template<bool IsFirstPass>
285 struct make_tree<2, 1, IsFirstPass> {
286 template<unsigned Remaining, class A, class B, class... Ts>
287 using f = typename make_tree
288 <
289 pick_next(Remaining - 2),
290 sizeof...(Ts) != 0,
292 >
293 ::template f
294 <
295 Remaining - 2,
296 Ts...,
298 >;
299 };
300
301 // only one type left, stop
302 template<bool F>
303 struct make_tree<0, 0, F> {
304 template<unsigned, class A>
305 using f = A;
306 };
307
308 // end of one pass, restart
309 template<bool IsFirstPass>
310 struct make_tree<0, 1, IsFirstPass> {
311 template<unsigned Remaining, class... Ts>
312 using f = typename make_tree
313 <
314 pick_next(sizeof...(Ts)),
315 (sizeof...(Ts) != 1),
316 false // <- both first pass and tail call recurse into a tail call
317 >
318 ::template f<sizeof...(Ts), Ts...>;
319 };
320
321 // one odd type left in the pass, put it at the back to preserve the order
322 template<>
323 struct make_tree<1, 1, false> {
324 template<unsigned Remaining, class A, class... Ts>
325 using f = typename make_tree<0, sizeof...(Ts) != 0, false>
326 ::template f<0, Ts..., A>;
327 };
328
329 // one odd type left in the first pass, wrap it in an union
330 template<>
331 struct make_tree<1, 1, true> {
332 template<unsigned, class A, class... Ts>
333 using f = typename make_tree<0, sizeof...(Ts) != 0, false>
334 ::template f<0, Ts..., union_node<true, A, dummy_type>>;
335 };
336
337 template<class... Ts>
338 using make_tree_union = typename
339 make_tree<pick_next(sizeof...(Ts)), 1, true>::template f<sizeof...(Ts), Ts...>;
340
341 // ============================================================
342
343 // Ts... must be sorted in ascending size
344 template<std::size_t Num, class... Ts>
346 type_pack_element<(static_cast<unsigned char>(Num > std::numeric_limits<Ts>::max()) + ...),
347 Ts...
348 >;
349
350 // why do we need this again? i think something to do with GCC?
351 namespace swap_trait {
352 using std::swap;
353
354 template<class A>
355 concept able = requires (A a, A b) { swap(a, b); };
356
357 template<class A>
358 inline constexpr bool nothrow = noexcept(swap(std::declval<A&>(), std::declval<A&>()));
359 }
360
361#ifndef PLUGIFY_VARIANT_NO_STD_HASH
362 template<class T>
363 inline constexpr bool has_std_hash = requires (T t) {
364 std::size_t(::std::hash<std::remove_cvref_t<T>>{}(t));
365 };
366#endif // PLUGIFY_VARIANT_NO_STD_HASH
367
368 template<class T>
369 inline constexpr T* addressof(T& obj) noexcept {
370#if PLUGIFY_COMPILER_GCC || PLUGIFY_COMPILER_CLANG
371 return __builtin_addressof(obj);
372#elif defined(PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE)
373 // if & is overloaded, use the ugly version
374 if constexpr (requires { obj.operator&(); })
375 return reinterpret_cast<T*>
376 (&const_cast<char&>(reinterpret_cast<const volatile char&>(obj)));
377 else
378 return &obj;
379#else
380 return std::addressof(obj);
381#endif
382 }
383
384 // ========================= visit dispatcher
385
386 template<class Fn, class... Vars>
387 using rtype_visit = decltype((std::declval<Fn>()(std::declval<Vars>().template unsafe_get<0>()...)));
388
389 template<class Fn, class Var>
390 using rtype_index_visit = decltype((std::declval<Fn>()(std::declval<Var>().template unsafe_get<0>(),
391 std::integral_constant<std::size_t, 0>{}))
392 );
393
394 inline namespace v1 {
395
396#define DEC(N) X((N)) X((N) + 1) X((N) + 2) X((N) + 3) X((N) + 4) X((N) + 5) X((N) + 6) X((N) + 7) X((N) + 8) X((N) + 9)
397
398#define SEQ30(N) DEC((N) + 0) DEC((N) + 10) DEC((N) + 20)
399#define SEQ100(N) SEQ30((N) + 0) SEQ30((N) + 30) SEQ30((N) + 60) DEC((N) + 90)
400#define SEQ200(N) SEQ100((N) + 0) SEQ100((N) + 100)
401#define SEQ400(N) SEQ200((N) + 0) SEQ200((N) + 200)
402#define CAT(M, N) M##N
403#define CAT2(M, N) CAT(M, N)
404#define INJECTSEQ(N) CAT2(SEQ, N)(0)
405
406 // single-visitation
407
408 template<unsigned Offset, class Rtype, class Fn, class V>
409 constexpr Rtype single_visit_tail(Fn&& fn, V&& v) {
410
411 constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
412
413#define X(N) case (N + Offset) : \
414 if constexpr (N < RemainingIndex) { \
415 return static_cast<Fn&&>(fn)(static_cast<V&&>(v).template unsafe_get<N+Offset>()); \
416 break; \
417 } else PLUGIFY_UNREACHABLE();
418
419#define SEQSIZE 100
420
421 switch (v.index()) {
422
423 INJECTSEQ(SEQSIZE)
424
425 default:
426 if constexpr (SEQSIZE < RemainingIndex)
427 return detail::single_visit_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
428 else
429 PLUGIFY_UNREACHABLE();
430 }
431
432#undef X
433#undef SEQSIZE
434 }
435
436 template<unsigned Offset, class Rtype, class Fn, class V>
437 constexpr Rtype single_visit_w_index_tail(Fn&& fn, V&& v) {
438
439 constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
440
441#define X(N) case (N + Offset) : \
442 if constexpr (N < RemainingIndex) { \
443 return static_cast<Fn&&>(fn)(static_cast<V&&>(v).template unsafe_get<N+Offset>(), std::integral_constant<unsigned, N+Offset>{}); \
444 break; \
445 } else PLUGIFY_UNREACHABLE();
446
447#define SEQSIZE 100
448
449 switch (v.index()) {
450
451 INJECTSEQ(SEQSIZE)
452
453 default:
454 if constexpr (SEQSIZE < RemainingIndex)
455 return detail::single_visit_w_index_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
456 else
457 PLUGIFY_UNREACHABLE();
458 }
459
460#undef X
461#undef SEQSIZE
462 }
463
464 template<class Fn, class V>
465 constexpr decltype(auto) visit(Fn&& fn, V&& v) {
466 return detail::single_visit_tail<0, rtype_visit<Fn&&, V&&>>(PLG_FWD(fn), PLG_FWD(v));
467 }
468
469 // unlike other visit functions, this takes the variant first!
470 // this is confusing, but make the client code easier to read
471 template<class Fn, class V>
472 constexpr decltype(auto) visit_with_index(V&& v, Fn&& fn) {
473 return detail::single_visit_w_index_tail<0, rtype_index_visit<Fn&&, V&&>>(PLG_FWD(fn), PLG_FWD(v));
474 }
475
476 template<class Fn, class Head, class... Tail>
477 constexpr decltype(auto) multi_visit(Fn&& fn, Head&& head, Tail&&... tail) {
478
479 // visit them one by one, starting with the last
480 auto vis = [&fn, &head] (auto&&... args) -> decltype(auto) {
481 return detail::visit([&fn, &args...] (auto&& elem) -> decltype(auto) {
482 return PLG_FWD(fn)(PLG_FWD(elem), PLG_FWD(args)...);
483 }, PLG_FWD(head));
484 };
485
486 if constexpr (sizeof...(tail) == 0)
487 return PLG_FWD(vis)();
488 else if constexpr (sizeof...(tail) == 1)
489 return detail::visit(PLG_FWD(vis), PLG_FWD(tail)...);
490 else
491 return detail::multi_visit(PLG_FWD(vis), PLG_FWD(tail)...);
492 }
493
494#undef DEC
495#undef SEQ30
496#undef SEQ100
497#undef SEQ200
498#undef SEQ400
499#undef CAT
500#undef CAT2
501#undef INJECTSEQ
502
503 } // inline namespace v1
504
506 template<class T>
507 constexpr bool operator==(T idx) const noexcept { return idx == std::numeric_limits<T>::max(); }
508 };
509 }
510
511 inline static constexpr detail::variant_npos_t variant_npos;
512
513 template<class... Ts>
514 class variant;
515
516 template<typename T>
517 struct is_variant_impl : std::false_type {};
518
519 template<typename... Ts>
520 struct is_variant_impl<variant<Ts...>> : std::true_type {};
521
522 template<typename T>
523 struct is_variant : is_variant_impl<std::decay_t<T>> {};
524
525 template<typename T>
526 inline constexpr bool is_variant_v = is_variant<T>::value;
527
528 // ill-formed variant, an empty specialization prevents some really bad errors messages on gcc
529 template<class... Ts>
530 requires (
531 (std::is_array_v<Ts> || ...)
532 || (std::is_reference_v<Ts> || ...)
533 || (std::is_void_v<Ts> || ...)
534 || sizeof...(Ts) == 0
535 )
536 class variant<Ts...> {
537 static_assert(sizeof...(Ts) > 0, "A variant cannot be empty.");
538 static_assert(not(std::is_reference_v<Ts> || ...), "A variant cannot contain references, consider using reference wrappers instead.");
539 static_assert(not(std::is_void_v<Ts> || ...), "A variant cannot contains void.");
540 static_assert(not(std::is_array_v<Ts> || ...), "A variant cannot contain a raw array type, consider using std::array instead.");
541 };
542
543 template<class... Ts>
544 class variant {
545 using storage = detail::union_node<false, detail::make_tree_union<Ts...>, detail::dummy_type>;
546
547 static constexpr bool is_trivial = std::is_trivial_v<storage>;
548 static constexpr bool has_copy_ctor = std::is_copy_constructible_v<storage>;
549 static constexpr bool trivial_copy_ctor = is_trivial || std::is_trivially_copy_constructible_v<storage>;
550 static constexpr bool has_copy_assign = std::is_copy_constructible_v<storage>;
551 static constexpr bool trivial_copy_assign = is_trivial || std::is_trivially_copy_assignable_v<storage>;
552 static constexpr bool has_move_ctor = std::is_move_constructible_v<storage>;
553 static constexpr bool trivial_move_ctor = is_trivial || std::is_trivially_move_constructible_v<storage>;
554 static constexpr bool has_move_assign = std::is_move_assignable_v<storage>;
555 static constexpr bool trivial_move_assign = is_trivial || std::is_trivially_move_assignable_v<storage>;
556 static constexpr bool trivial_dtor = std::is_trivially_destructible_v<storage>;
557
558 public:
559 template<std::size_t Idx>
560 using alternative = std::remove_reference_t<decltype(std::declval<storage&>().template get<Idx>())>;
561
562 static constexpr bool can_be_valueless = not is_trivial;
563
564 static constexpr unsigned size = sizeof...(Ts);
565
566 using index_type = detail::smallest_suitable_integer_type<sizeof...(Ts) + can_be_valueless, unsigned char, unsigned short, unsigned>;
567
568 static constexpr index_type npos = static_cast<index_type>(-1);
569
570 template<class T>
571 static constexpr int index_of = detail::find_first_true({std::is_same_v<T, Ts>...});
572
573 // ============================================= constructors (20.7.3.2)
574
575 // default constructor
576 constexpr variant()
577 noexcept(std::is_nothrow_default_constructible_v<alternative<0>>)
578 requires std::is_default_constructible_v<alternative<0>>
579 : _storage{in_place_index<0>}, _current{0}
580 {}
581
582 // copy constructor (trivial)
583 constexpr variant(const variant&)
584 requires trivial_copy_ctor
585 = default;
586
587 // note : both the copy and move constructor cannot be meaningfully constexpr without std::construct_at
588 // copy constructor
589 constexpr variant(const variant& o)
590 requires (has_copy_ctor and not trivial_copy_ctor)
591 : _storage{detail::dummy_type{}} {
592 construct_from(o);
593 }
594
595 // move constructor (trivial)
596 constexpr variant(variant&&)
597 requires trivial_move_ctor
598 = default;
599
600 // move constructor
601 constexpr variant(variant&& o)
602 noexcept((std::is_nothrow_move_constructible_v<Ts> && ...))
603 requires (has_move_ctor and not trivial_move_ctor)
604 : _storage{detail::dummy_type{}} {
605 construct_from(static_cast<variant&&>(o));
606 }
607
608 // generic constructor
609 template<class T, class M = detail::best_overload_match<T&&, Ts...>, class D = std::decay_t<T>>
610 constexpr variant(T&& t)
611 noexcept(std::is_nothrow_constructible_v<M, T&&>)
612 requires (not std::is_same_v<D, variant> and not std::is_base_of_v<detail::emplacer_tag, D>)
613 : variant{in_place_index<index_of<M>>, static_cast<T&&>(t)}
614 {}
615
616 // construct at index
617 template<std::size_t Index, class... Args>
619 explicit constexpr variant(in_place_index_t<Index> tag, Args&&... args)
620 : _storage{tag, static_cast<Args&&>(args)...}, _current(Index)
621 {}
622
623 // construct a given type
624 template<class T, class... Args>
625 requires (detail::appears_exactly_once<T, Ts...> && std::is_constructible_v<T, Args&&...>)
626 explicit constexpr variant(in_place_type_t<T>, Args&&... args)
627 : variant{in_place_index<index_of<T>>, static_cast<Args&&>(args)...}
628 {}
629
630 // initializer-list constructors
631 template<std::size_t Index, class U, class... Args>
632 requires (
633 (Index < size) and
634 std::is_constructible_v<alternative<Index>, std::initializer_list<U>&, Args&&...>
635 )
636 explicit constexpr variant(in_place_index_t<Index> tag, std::initializer_list<U> list, Args&&... args)
637 : _storage{tag, list, PLG_FWD(args)...}, _current{Index}
638 {}
639
640 template<class T, class U, class... Args>
641 requires (
642 detail::appears_exactly_once<T, Ts...>
643 && std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>
644 )
645 explicit constexpr variant(in_place_type_t<T>, std::initializer_list<U> list, Args&&... args)
646 : _storage{in_place_index<index_of<T>>, list, PLG_FWD(args)...}, _current{index_of<T> }
647 {}
648
649 // ================================ destructors (20.7.3.3)
650
651 constexpr ~variant() requires trivial_dtor = default;
652
653 constexpr ~variant() requires (not trivial_dtor) {
654 reset();
655 }
656
657 // ================================ assignment(20.7.3.4)
658
659 // copy assignment(trivial)
660 constexpr variant& operator=(const variant& o)
661 requires trivial_copy_assign && trivial_copy_ctor
662 = default;
663
664 // copy assignment
665 constexpr variant& operator=(const variant& rhs)
666 requires (has_copy_assign and not(trivial_copy_assign && trivial_copy_ctor)) {
667 if (this == &rhs) [[unlikely]]
668 return *this;
669
670 assign_from(rhs, [&](const auto& elem, auto index_cst) {
671 if (index() == index_cst)
673 else {
674 using type = alternative<index_cst>;
675 constexpr bool do_simple_copy = std::is_nothrow_copy_constructible_v<type> //-V001
676 or not std::is_nothrow_move_constructible_v<type>;
677 if constexpr (do_simple_copy)
679 else {
681 emplace<index_cst>(PLG_MOV(tmp));
682 }
683 }
684 });
685 return *this;
686 }
687
688 // move assignment(trivial)
689 constexpr variant& operator=(variant&& o)
690 requires (trivial_move_assign and trivial_move_ctor and trivial_dtor)
691 = default;
692
693 // move assignment
694 constexpr variant& operator=(variant&& o)
695 noexcept((std::is_nothrow_move_constructible_v<Ts> && ...) && (std::is_nothrow_move_assignable_v<Ts> && ...))
696 requires (has_move_assign && has_move_ctor and not(trivial_move_assign and trivial_move_ctor and trivial_dtor)) {
697 if (this == &o) [[unlikely]]
698 return *this;
699
700 assign_from(PLG_FWD(o), [&](auto&& elem, auto index_cst) {
701 if (index() == index_cst)
702 unsafe_get<index_cst>() = PLG_MOV(elem);
703 else
704 emplace<index_cst>(PLG_MOV(elem));
705 });
706 return *this;
707 }
708
709 // generic assignment
710 template<class T>
712 constexpr variant& operator=(T&& t)
713 noexcept(std::is_nothrow_assignable_v<detail::best_overload_match<T&&, Ts...>, T&&>
714 && std::is_nothrow_constructible_v<detail::best_overload_match<T&&, Ts...>, T&&>) {
715 using related_type = detail::best_overload_match<T&&, Ts...>;
716 constexpr auto new_index = index_of<related_type>;
717
718 if (_current == new_index)
719 unsafe_get<new_index>() = PLG_FWD(t);
720 else {
721 constexpr bool do_simple_emplace =
722 std::is_nothrow_constructible_v<related_type, T>
723 or not std::is_nothrow_move_constructible_v<related_type>;
724
725 if constexpr (do_simple_emplace)
726 emplace<new_index>(PLG_FWD(t));
727 else {
729 emplace<new_index>(PLG_MOV(tmp));
730 }
731 }
732
733 return *this;
734 }
735
736 // ================================== modifiers (20.7.3.5)
737
738 template<class T, class... Args>
739 requires (std::is_constructible_v<T, Args&&...> && detail::appears_exactly_once<T, Ts...>)
740 constexpr T& emplace(Args&&... args) {
741 return emplace<index_of<T>>(static_cast<Args&&>(args)...);
742 }
743
744 template<std::size_t Idx, class... Args>
746 constexpr auto& emplace(Args&&... args) {
747 return emplace_impl<Idx>(PLG_FWD(args)...);
748 }
749
750 // emplace with initializer-lists
751 template<std::size_t Idx, class U, class... Args>
752 requires (Idx < size
753 && std::is_constructible_v<alternative<Idx>, std::initializer_list<U>&, Args&&...>)
754 constexpr auto& emplace(std::initializer_list<U> list, Args&&... args) {
755 return emplace_impl<Idx>(list, PLG_FWD(args)...);
756 }
757
758 template<class T, class U, class... Args>
759 requires (std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>
760 && detail::appears_exactly_once<T, Ts...>)
761 constexpr T& emplace(std::initializer_list<U> list, Args&&... args) {
762 return emplace_impl<index_of<T>>(list, PLG_FWD(args)...);
763 }
764
765 // ==================================== value status (20.7.3.6)
766
767 constexpr bool valueless_by_exception() const noexcept {
768 if constexpr (can_be_valueless)
769 return _current == npos;
770 else return false;
771 }
772
773 constexpr index_type index() const noexcept {
774 return _current;
775 }
776
777 // =================================== swap (20.7.3.7)
778
779 constexpr void swap(variant& o)
780 noexcept((std::is_nothrow_move_constructible_v<Ts> && ...)
781 && (detail::swap_trait::template nothrow<Ts> && ...))
782 requires (has_move_ctor && (detail::swap_trait::template able<Ts> && ...)) {
783 if constexpr (can_be_valueless) {
784 // if one is valueless, move the element form the non-empty variant,
785 // reset it, and set it to valueless
786 constexpr auto impl_one_valueless = [](auto&& full, auto& empty) {
787 detail::visit_with_index(PLG_FWD(full), detail::emplace_no_dtor_from_elem<variant&>{empty});
788 full.reset_no_check();
789 full._current = npos;
790 };
791
792 switch(static_cast<int>(index() == npos) + static_cast<int>(o.index() == npos) * 2) {
793 case 0 :
794 break;
795 case 1 :
796 // "this" is valueless
797 impl_one_valueless(PLG_MOV(o), *this);
798 return;
799 case 2 :
800 // "other" is valueless
801 impl_one_valueless(PLG_MOV(*this), o);
802 return;
803 case 3 :
804 // both are valueless, do nothing
805 return;
806 }
807 }
808
809 assert(not(valueless_by_exception() && o.valueless_by_exception()));
810
811 detail::visit_with_index(o, [&o, this](auto&& elem, auto index_cst) {
812 if (index() == index_cst) {
813 using std::swap;
815 return;
816 }
817
818 using idx_t = decltype(index_cst);
819 detail::visit_with_index(*this, [this, &o, &elem](auto&& this_elem, auto this_index) {
820 auto tmp { PLG_MOV(this_elem) };
821
822 // destruct the element
823 detail::destruct<alternative<this_index>>(this_elem);
824
825 // ok, we just destroyed the element in this, don't call the dtor again
827
828 // we could refactor this
829 detail::destruct<alternative<idx_t::value>>(elem);
831 });
832 });
833 }
834
835 // +================================== methods for internal use
836 // these methods performs no errors checking at all
837
838 template<detail::union_index_t Idx>
839 constexpr auto& unsafe_get() & noexcept {
840 static_assert(Idx < size);
841 assert(_current == Idx);
842 return _storage.template get<Idx>();
843 }
844
845 template<detail::union_index_t Idx>
846 constexpr auto&& unsafe_get() && noexcept {
847 static_assert(Idx < size);
848 assert(_current == Idx);
849 return PLG_MOV(_storage.template get<Idx>());
850 }
851
852 template<detail::union_index_t Idx>
853 constexpr const auto& unsafe_get() const & noexcept {
854 static_assert(Idx < size);
855 assert(_current == Idx);
856 return _storage.template get<Idx>();
857 }
858
859 template<detail::union_index_t Idx>
860 constexpr const auto&& unsafe_get() const && noexcept {
861 static_assert(Idx < size);
862 assert(_current == Idx);
863 return PLG_MOV(_storage.template get<Idx>());
864 }
865
866 private:
867 // assign from another variant
868 template<class Other, class Fn>
869 constexpr void assign_from(Other&& o, Fn&& fn) {
870 if constexpr (can_be_valueless) {
871 if (o.index() == npos) {
872 if (_current != npos) {
873 reset_no_check();
874 _current = npos;
875 }
876 return;
877 }
878 }
879 assert(not o.valueless_by_exception());
880 detail::visit_with_index(PLG_FWD(o), PLG_FWD(fn));
881 }
882
883 template<unsigned Idx, class... Args>
884 constexpr auto& emplace_impl(Args&&... args) {
885 reset();
886 emplace_no_dtor<Idx>(PLG_FWD(args)...);
887 return unsafe_get<Idx>();
888 }
889
890 // can be used directly only when the variant is in a known empty state
891 template<unsigned Idx, class... Args>
892 constexpr void emplace_no_dtor(Args&&... args) {
893 using T = alternative<Idx>;
894
895 if constexpr (not std::is_nothrow_constructible_v<T, Args&&...>)
896 {
897 if constexpr (std::is_nothrow_move_constructible_v<T>)
898 {
899 do_emplace_no_dtor<Idx>(T{PLG_FWD(args)...});
900 }
901 else if constexpr (std::is_nothrow_copy_constructible_v<T>)
902 {
903 T tmp {PLG_FWD(args)...};
905 }
906 else
907 {
908 static_assert(can_be_valueless && (Idx == Idx),
909 "Internal error : the possibly valueless branch of emplace was taken despite |can_be_valueless| being false");
910 _current = npos;
911 do_emplace_no_dtor<Idx>(PLG_FWD(args)...);
912 }
913 }
914 else
915 do_emplace_no_dtor<Idx>(PLG_FWD(args)...);
916 }
917
918 template<unsigned Idx, class... Args>
919 constexpr void do_emplace_no_dtor(Args&&... args) {
920 _current = static_cast<index_type>(Idx);
921
922 auto* ptr = detail::addressof(unsafe_get<Idx>());
923
924#ifdef PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
925 using T = alternative<Idx>;
926 new (const_cast<void*>(ptr)) t(PLG_FWD(args)...);
927#else
928 std::construct_at(ptr, PLG_FWD(args)...);
929#endif // PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
930 }
931
932 // destroy the current elem IFF not valueless
933 constexpr void reset() {
934 if constexpr (can_be_valueless)
935 if (valueless_by_exception()) return;
936 reset_no_check();
937 }
938
939 // destroy the current element without checking for valueless
940 constexpr void reset_no_check() {
941 assert(index() < size);
942 if constexpr (not trivial_dtor) {
943 detail::visit_with_index(*this, [](auto& elem, auto index_cst) {
944 detail::destruct<alternative<index_cst>>(elem);
945 });
946 }
947 }
948
949 // construct this from another variant, for constructors only
950 template<class Other>
951 constexpr void construct_from(Other&& o) {
952 if constexpr (can_be_valueless)
953 if (o.valueless_by_exception()) {
954 _current = npos;
955 return;
956 }
957
958 detail::visit_with_index(PLG_FWD(o), detail::emplace_no_dtor_from_elem<variant&>{*this});
959 }
960
961 template<class T>
963
964 storage _storage;
965#if INTPTR_MAX == INT32_MAX
966 volatile char pad[8];
967#endif
968 index_type _current;
969 };
970
971 // ================================= value access (20.7.5)
972
973 template<class T, class... Ts>
974 constexpr bool holds_alternative(const variant<Ts...>& v) noexcept {
975 static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
976 constexpr auto Index = variant<Ts...>::template index_of<T>;
977 return v.index() == Index;
978 }
979
980 // ========= get by index
981
982 template<std::size_t Idx, class... Ts>
983 constexpr auto& get(variant<Ts...>& v) {
984 static_assert(Idx < sizeof...(Ts), "Index exceeds the variant size. ");
985 PLUGIFY_ASSERT(v.index() == Idx, "plg::variant:get(): Bad variant access in get.", bad_variant_access);
986 return (v.template unsafe_get<Idx>());
987 }
988
989 template<std::size_t Idx, class... Ts>
990 constexpr const auto& get(const variant<Ts...>& v) {
991 return plg::get<Idx>(const_cast<variant<Ts...>&>(v));
992 }
993
994 template<std::size_t Idx, class... Ts>
995 constexpr auto&& get(variant<Ts...>&& v) {
996 return PLG_MOV(plg::get<Idx>(v));
997 }
998
999 template<std::size_t Idx, class... Ts>
1000 constexpr const auto&& get(const variant<Ts...>&& v) {
1001 return PLG_MOV(plg::get<Idx>(v));
1002 }
1003
1004 // ========= get by type
1005
1006 template<class T, class... Ts>
1007 constexpr T& get(variant<Ts...>& v) {
1008 return plg::get<variant<Ts...>::template index_of<T> >(v);
1009 }
1010
1011 template<class T, class... Ts>
1012 constexpr const T& get(const variant<Ts...>& v) {
1013 return plg::get<variant<Ts...>::template index_of<T> >(v);
1014 }
1015
1016 template<class T, class... Ts>
1017 constexpr T&& get(variant<Ts...>&& v) {
1018 return plg::get<variant<Ts...>::template index_of<T> >(PLG_FWD(v));
1019 }
1020
1021 template<class T, class... Ts>
1022 constexpr const T&& get(const variant<Ts...>&& v) {
1023 return plg::get<variant<Ts...>::template index_of<T> >(PLG_FWD(v));
1024 }
1025
1026 // ===== get_if by index
1027
1028 template<std::size_t Idx, class... Ts>
1029 constexpr const auto* get_if(const variant<Ts...>* v) noexcept {
1030 using rtype = typename variant<Ts...>::template alternative<Idx>*;
1031 if (v == nullptr || v->index() != Idx)
1032 return rtype{nullptr};
1033 else
1034 return detail::addressof(v->template unsafe_get<Idx>());
1035 }
1036
1037 template<std::size_t Idx, class... Ts>
1038 constexpr auto* get_if(variant<Ts...>* v) noexcept {
1039 using rtype = typename variant<Ts...>::template alternative<Idx>;
1040 return const_cast<rtype*>(
1041 plg::get_if<Idx>(static_cast<const variant<Ts...>*>(v))
1042 );
1043 }
1044
1045 // ====== get_if by type
1046
1047 template<class T, class... Ts>
1048 constexpr T* get_if(variant<Ts...>* v) noexcept {
1049 static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
1050 return plg::get_if<variant<Ts...>::template index_of<T> >(v);
1051 }
1052
1053 template<class T, class... Ts>
1054 constexpr const T* get_if(const variant<Ts...>* v) noexcept {
1055 static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
1056 return plg::get_if<variant<Ts...>::template index_of<T> >(v);
1057 }
1058
1059 // =============================== visitation (20.7.7)
1060
1061 template<class Fn, class... Vs>
1062 constexpr decltype(auto) visit(Fn&& fn, Vs&&... vs) {
1063 if constexpr ((std::decay_t<Vs>::can_be_valueless || ...))
1064 PLUGIFY_ASSERT(!(vs.valueless_by_exception() || ...), "plg::variant:visit(): Bad variant access in visit.", bad_variant_access);
1065
1066 if constexpr (sizeof...(Vs) == 1)
1067 return detail::visit(PLG_FWD(fn), PLG_FWD(vs)...);
1068 else
1069 return detail::multi_visit(PLG_FWD(fn), PLG_FWD(vs)...);
1070 }
1071
1072 template<class Fn>
1073 constexpr decltype(auto) visit(Fn&& fn) {
1074 return PLG_FWD(fn)();
1075 }
1076
1077 template<class R, class Fn, class... Vs>
1078 requires (is_variant_v<Vs> && ...)
1079 constexpr R visit(Fn&& fn, Vs&&... vars) {
1080 return static_cast<R>(plg::visit(PLG_FWD(fn), PLG_FWD(vars)...));
1081 }
1082
1083 // ============================== relational operators (20.7.6)
1084
1085 template<class... Ts>
1086 requires (detail::has_eq_comp<Ts> && ...)
1087 constexpr bool operator==(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1088 if (v1.index() != v2.index())
1089 return false;
1090 if constexpr (variant<Ts...>::can_be_valueless)
1091 if (v1.valueless_by_exception()) return true;
1092 return detail::visit_with_index(v2, [&v1](auto& elem, auto index) -> bool {
1093 return (v1.template unsafe_get<index>() == elem);
1094 });
1095 }
1096
1097 template<class... Ts>
1098 constexpr bool operator!=(const variant<Ts...>& v1, const variant<Ts...>& v2)
1099 requires requires { v1 == v2; }
1100 {
1101 return not(v1 == v2);
1102 }
1103
1104 template<class... Ts>
1105 requires (detail::has_lesser_comp<const Ts&> && ...)
1106 constexpr bool operator<(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1107 if constexpr (variant<Ts...>::can_be_valueless) {
1108 if (v2.valueless_by_exception()) return false;
1109 if (v1.valueless_by_exception()) return true;
1110 }
1111 if (v1.index() == v2.index()) {
1112 return detail::visit_with_index(v1, [&v2](auto& elem, auto index) -> bool {
1113 return (elem < v2.template unsafe_get<index>());
1114 });
1115 }
1116 else
1117 return (v1.index() < v2.index());
1118 }
1119
1120 template<class... Ts>
1121 constexpr bool operator>(const variant<Ts...>& v1, const variant<Ts...>& v2)
1122 requires requires { v2 < v1; }
1123 {
1124 return v2 < v1;
1125 }
1126
1127 template<class... Ts>
1128 requires (detail::has_less_or_eq_comp<const Ts&> && ...)
1129 constexpr bool operator<=(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1130 if constexpr (variant<Ts...>::can_be_valueless) {
1131 if (v1.valueless_by_exception()) return true;
1132 if (v2.valueless_by_exception()) return false;
1133 }
1134 if (v1.index() == v2.index()) {
1135 return detail::visit_with_index(v1, [&v2](auto& elem, auto index) -> bool {
1136 return (elem <= v2.template unsafe_get<index>());
1137 });
1138 }
1139 else
1140 return (v1.index() < v2.index());
1141 }
1142
1143 template<class... Ts>
1144 constexpr bool operator>=(const variant<Ts...>& v1, const variant<Ts...>& v2)
1145 requires requires { v2 <= v1; }
1146 {
1147 return v2 <= v1;
1148 }
1149
1150 // ===================================== monostate (20.7.8, 20.7.9)
1151
1152 struct monostate{};
1153 constexpr bool operator==(monostate, monostate) noexcept { return true; }
1154 constexpr bool operator> (monostate, monostate) noexcept { return false; }
1155 constexpr bool operator< (monostate, monostate) noexcept { return false; }
1156 constexpr bool operator<=(monostate, monostate) noexcept { return true; }
1157 constexpr bool operator>=(monostate, monostate) noexcept { return true; }
1158
1159 // ===================================== specialized algorithms (20.7.10)
1160
1161 template<class... Ts>
1162 constexpr void swap(variant<Ts...>& a, variant<Ts...>& b)
1163 noexcept(noexcept(a.swap(b)))
1164 requires requires { a.swap(b); }
1165 {
1166 a.swap(b);
1167 }
1168
1169 // ===================================== helper classes (20.7.4)
1170
1171 template<class T>
1172 requires is_variant_v<T>
1173 inline constexpr std::size_t variant_size_v = std::decay_t<T>::size;
1174
1175 // not sure why anyone would need this, i'm adding it anyway
1176 template<class T>
1177 requires is_variant_v<T>
1178 struct variant_size : std::integral_constant<std::size_t, variant_size_v<T>> {};
1179
1180 namespace detail {
1181 // ugh, we have to take care of volatile here
1182 template<bool IsVolatile>
1183 struct var_alt_impl {
1184 template<std::size_t Idx, class T>
1185 using type = std::remove_reference_t<decltype(std::declval<T>().template unsafe_get<Idx>())>;
1186 };
1187
1188 template<>
1189 struct var_alt_impl<true> {
1190 template<std::size_t Idx, class T>
1191 using type = volatile typename var_alt_impl<false>::template type<Idx, std::remove_volatile_t<T>>;
1192 };
1193 }
1194
1195 template<std::size_t Idx, class T>
1196 requires (Idx < variant_size_v<T>)
1197 using variant_alternative_t = typename detail::var_alt_impl<std::is_volatile_v<T>>::template type<Idx, T>;
1198
1199 template<std::size_t Idx, class T>
1200 requires is_variant_v<T>
1204
1205 // ===================================== extensions (unsafe_get)
1206
1207 template<std::size_t Idx, class Var>
1208 requires is_variant_v<Var>
1209 constexpr auto&& unsafe_get(Var&& var) noexcept {
1210 static_assert(Idx < std::decay_t<Var>::size, "Index exceeds the variant size.");
1211 return PLG_FWD(var).template unsafe_get<Idx>();
1212 }
1213
1214 template<class T, class Var>
1215 requires is_variant_v<Var>
1216 constexpr auto&& unsafe_get(Var&& var) noexcept {
1217 return plg::unsafe_get<std::decay_t<Var>::template index_of<T>>(PLG_FWD(var));
1218 }
1219
1220} // namespace plg
1221
1222// ====================================== hash support(20.7.12)
1223#ifndef PLUGIFY_VARIANT_NO_STD_HASH
1224namespace std {
1225 template<class... Ts>
1226 requires (plg::detail::has_std_hash<Ts> && ...)
1227 struct hash<plg::variant<Ts...>> {
1228 std::size_t operator()(const plg::variant<Ts...>& v) const {
1229 if constexpr (plg::variant<Ts...>::can_be_valueless)
1230 if (v.valueless_by_exception())
1231 return static_cast<std::size_t>(-1);
1232
1233 return plg::detail::visit_with_index(v, [](auto& elem, auto index_) {
1234 using type = std::remove_cvref_t<decltype(elem)>;
1235 return std::hash<type>{}(elem) + index_;
1236 });
1237 }
1238 };
1239
1240 template<>
1241 struct hash<plg::monostate> {
1242 constexpr std::size_t operator()(plg::monostate) const noexcept { return static_cast<size_t>(-1); }
1243 };
1244} // namespace std
1245#endif // PLUGIFY_VARIANT_NO_STD_HASH
1246
1247#undef PLG_FWD
1248#undef PLG_MOV