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