plugify  1.0.0.0
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 <plugify/macro.hpp>
24 
25 namespace plg {
26 #if PLUGIFY_EXCEPTIONS
27  class bad_variant_access final : public std::exception {
28  const char* message = ""; // llvm test requires a well formed what() on default init
29  public :
30  explicit bad_variant_access(const char* str) noexcept : message{str} {}
31  bad_variant_access() noexcept = default;
32  bad_variant_access(const bad_variant_access&) noexcept = default;
33  bad_variant_access& operator=(const bad_variant_access&) noexcept = default;
34  const char* what() const noexcept override { return message; }
35  };
36 #endif // PLUGIFY_EXCEPTIONS
37 
38  namespace detail {
39  struct variant_tag{};
40  struct emplacer_tag{};
41  }
42 
43  template<class T>
45 
46  template<std::size_t Index>
48 
49  template<std::size_t Index>
50  inline static constexpr in_place_index_t<Index> in_place_index;
51 
52  template<class T>
53  inline static constexpr in_place_type_t<T> in_place_type;
54 
55  namespace detail {
56  template<int N>
57  constexpr int find_first_true(bool (&&arr)[N]) {
58  for (int k = 0; k < N; ++k)
59  if (arr[k])
60  return k;
61  return -1;
62  }
63 
64  template<class T, class... Ts>
65  inline constexpr bool appears_exactly_once = (static_cast<unsigned short>(std::is_same_v<T, Ts>) + ...) == 1;
66 
67  // ============= type pack element
68 
69 #if __has_builtin(__type_pack_element)
70  template<std::size_t K, class... Ts>
71  using type_pack_element = __type_pack_element<K, Ts...>;
72 #else
73  template<unsigned char = 1>
74  struct find_type_i;
75 
76  template<>
77  struct find_type_i<1> {
78  template<std::size_t Idx, class T, class... Ts>
79  using f = typename find_type_i<(Idx != 1)>::template f<Idx - 1, Ts...>;
80  };
81 
82  template<>
83  struct find_type_i<0> {
84  template<std::size_t, class T, class... Ts>
85  using f = T;
86  };
87 
88  template<std::size_t K, class... Ts>
89  using type_pack_element = typename find_type_i<(K != 0)>::template f<K, Ts...>;
90 #endif
91 
92  // ============= overload match detector. to be used for variant generic assignment
93 
94  template<class T>
95  using arr1 = T[1];
96 
97  template<std::size_t N, class A>
98  struct overload_frag {
99  using type = A;
100  template<class T>
101  requires requires { arr1<A>{std::declval<T>()}; }
102  auto operator()(A, T&&) -> overload_frag<N, A>;
103  };
104 
105  template<class Seq, class... Args>
107 
108  template<std::size_t... Idx, class... Args>
109  struct make_overload<std::integer_sequence<std::size_t, Idx...>, Args...>
110  : overload_frag<Idx, Args>... {
112  };
113 
114  template<class T, class... Ts>
115  using best_overload_match = typename decltype(
116  make_overload<std::make_index_sequence<sizeof...(Ts)>, Ts...>{}
117  (std::declval<T>(), std::declval<T>())
118  )::type;
119 
120  template<class T, class... Ts>
121  concept has_non_ambiguous_match =
122  requires { typename best_overload_match<T, Ts...>; };
123 
124  // ================================== rel ops
125 
126  template<class From, class To>
127  concept convertible = std::is_convertible_v<From, To>;
128 
129  template<class T>
130  concept has_eq_comp = requires (T a, T b) {
131  { a == b } -> convertible<bool>;
132  };
133 
134  template<class T>
135  concept has_lesser_comp = requires (T a, T b) {
136  { a < b } -> convertible<bool>;
137  };
138 
139  template<class T>
140  concept has_less_or_eq_comp = requires (T a, T b) {
141  { a <= b } -> convertible<bool>;
142  };
143 
144  template<class A>
146  template<class T>
147  constexpr void operator()(T&& elem, auto index_) const {
148  a.template emplace_no_dtor<index_>(static_cast<T&&>(elem));
149  }
150  A& a;
151  };
152 
153  template<class E, class T>
154  constexpr void destruct(T& obj) {
155  if constexpr (not std::is_trivially_destructible_v<E>)
156  obj.~E();
157  }
158 
159  // =============================== variant union types
160 
161  // =================== base variant storage type
162  // this type is used to build a N-ary tree of union.
163 
164  struct dummy_type{ static constexpr int elem_size = 0; }; // used to fill the back of union nodes
165 
166  using union_index_t = unsigned;
167 
168 #define TRAIT(trait) (std::is_##trait##_v<A> && std::is_##trait##_v<B>)
169 
170 #define SFM(signature, trait) \
171  signature = default; \
172  signature requires (TRAIT(trait) and not TRAIT(trivially_##trait)) {}
173 
174 // given the two members of type A and B of an union X
175 // this create the proper conditionally trivial special members functions
176 #define INJECT_UNION_SFM(X) \
177  SFM(constexpr X (const X &), copy_constructible) \
178  SFM(constexpr X (X&&) noexcept, move_constructible) \
179  SFM(constexpr X& operator=(const X&), copy_assignable) \
180  SFM(constexpr X& operator=(X&&) noexcept, move_assignable) \
181  SFM(constexpr ~X(), destructible)
182 
183  template<bool IsLeaf>
184  struct node_trait;
185 
186  template<>
187  struct node_trait<true> {
188 
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<>
197  struct node_trait<false> {
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>
217  requires (node_trait<IsLeaf>::template ctor_branch<Index, A> == 1)
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>
223  requires (node_trait<IsLeaf>::template ctor_branch<Index, A> == 2)
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)
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,
290  IsFirstPass
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>
344  using smallest_suitable_integer_type =
345  type_pack_element<(static_cast<unsigned char>(Num > std::numeric_limits<Ts>::max()) + ...),
346  Ts...
347  >;
348 
349  // why do we need this again? i think something to do with GCC?
350  namespace swap_trait {
351  using std::swap;
352 
353  template<class A>
354  concept able = requires (A a, A b) { swap(a, b); };
355 
356  template<class A>
357  inline constexpr bool nothrow = noexcept(swap(std::declval<A&>(), std::declval<A&>()));
358  }
359 
360 #ifndef PLUGIFY_VARIANT_NO_STD_HASH
361  template<class T>
362  inline constexpr bool has_std_hash = requires (T t) {
363  std::size_t(::std::hash<std::remove_cvref_t<T>>{}(t));
364  };
365 #endif // PLUGIFY_VARIANT_NO_STD_HASH
366 
367  template<class T>
368  inline constexpr T* addressof(T& obj) noexcept {
369 #if defined(__GNUC__) || defined(__clang__)
370  return __builtin_addressof(obj);
371 #elif defined(PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE)
372  // if & is overloaded, use the ugly version
373  if constexpr (requires { obj.operator&(); })
374  return reinterpret_cast<T*>
375  (&const_cast<char&>(reinterpret_cast<const volatile char&>(obj)));
376  else
377  return &obj;
378 #else
379  return std::addressof(obj);
380 #endif
381  }
382 
383  // ========================= visit dispatcher
384 
385  template<class Fn, class... Vars>
386  using rtype_visit = decltype((std::declval<Fn>()(std::declval<Vars>().template unsafe_get<0>()...)));
387 
388  template<class Fn, class Var>
389  using rtype_index_visit = decltype((std::declval<Fn>()(std::declval<Var>().template unsafe_get<0>(),
390  std::integral_constant<std::size_t, 0>{}))
391  );
392 
393  inline namespace v1 {
394 
395 #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)
396 
397 #define SEQ30(N) DEC((N) + 0) DEC((N) + 10) DEC((N) + 20)
398 #define SEQ100(N) SEQ30((N) + 0) SEQ30((N) + 30) SEQ30((N) + 60) DEC((N) + 90)
399 #define SEQ200(N) SEQ100((N) + 0) SEQ100((N) + 100)
400 #define SEQ400(N) SEQ200((N) + 0) SEQ200((N) + 200)
401 #define CAT(M, N) M##N
402 #define CAT2(M, N) CAT(M, N)
403 #define INJECTSEQ(N) CAT2(SEQ, N)(0)
404 
405  // single-visitation
406 
407  template<unsigned Offset, class Rtype, class Fn, class V>
408  constexpr Rtype single_visit_tail(Fn&& fn, V&& v) {
409 
410  constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
411 
412 #define X(N) case (N + Offset) : \
413  if constexpr (N < RemainingIndex) { \
414  return static_cast<Fn&&>(fn)(static_cast<V&&>(v).template unsafe_get<N+Offset>()); \
415  break; \
416  } else PLUGIFY_UNREACHABLE();
417 
418 #define SEQSIZE 100
419 
420  switch (v.index()) {
421 
422  INJECTSEQ(SEQSIZE)
423 
424  default:
425  if constexpr (SEQSIZE < RemainingIndex)
426  return detail::single_visit_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
427  else
428  PLUGIFY_UNREACHABLE();
429  }
430 
431 #undef X
432 #undef SEQSIZE
433  }
434 
435  template<unsigned Offset, class Rtype, class Fn, class V>
436  constexpr Rtype single_visit_w_index_tail(Fn&& fn, V&& v) {
437 
438  constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
439 
440 #define X(N) case (N + Offset) : \
441  if constexpr (N < RemainingIndex) { \
442  return static_cast<Fn&&>(fn)(static_cast<V&&>(v).template unsafe_get<N+Offset>(), std::integral_constant<unsigned, N+Offset>{}); \
443  break; \
444  } else PLUGIFY_UNREACHABLE();
445 
446 #define SEQSIZE 100
447 
448  switch (v.index()) {
449 
450  INJECTSEQ(SEQSIZE)
451 
452  default:
453  if constexpr (SEQSIZE < RemainingIndex)
454  return detail::single_visit_w_index_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
455  else
456  PLUGIFY_UNREACHABLE();
457  }
458 
459 #undef X
460 #undef SEQSIZE
461  }
462 
463  template<class Fn, class V>
464  constexpr decltype(auto) visit(Fn&& fn, V&& v) {
465  return detail::single_visit_tail<0, rtype_visit<Fn&&, V&&>>(PLG_FWD(fn), PLG_FWD(v));
466  }
467 
468  // unlike other visit functions, this takes the variant first!
469  // this is confusing, but make the client code easier to read
470  template<class Fn, class V>
471  constexpr decltype(auto) visit_with_index(V&& v, Fn&& fn) {
472  return detail::single_visit_w_index_tail<0, rtype_index_visit<Fn&&, V&&>>(PLG_FWD(fn), PLG_FWD(v));
473  }
474 
475  template<class Fn, class Head, class... Tail>
476  constexpr decltype(auto) multi_visit(Fn&& fn, Head&& head, Tail&&... tail) {
477 
478  // visit them one by one, starting with the last
479  auto vis = [&fn, &head] (auto&&... args) -> decltype(auto) {
480  return detail::visit([&fn, &args...] (auto&& elem) -> decltype(auto) {
481  return PLG_FWD(fn)(PLG_FWD(elem), PLG_FWD(args)...);
482  }, PLG_FWD(head));
483  };
484 
485  if constexpr (sizeof...(tail) == 0)
486  return PLG_FWD(vis)();
487  else if constexpr (sizeof...(tail) == 1)
488  return detail::visit(PLG_FWD(vis), PLG_FWD(tail)...);
489  else
490  return detail::multi_visit(PLG_FWD(vis), PLG_FWD(tail)...);
491  }
492 
493 #undef DEC
494 #undef SEQ30
495 #undef SEQ100
496 #undef SEQ200
497 #undef SEQ400
498 #undef CAT
499 #undef CAT2
500 #undef INJECTSEQ
501 
502  } // inline namespace v1
503 
504  struct variant_npos_t {
505  template<class T>
506  constexpr bool operator==(T idx) const noexcept { return idx == std::numeric_limits<T>::max(); }
507  };
508  }
509 
510  inline static constexpr detail::variant_npos_t variant_npos;
511 
512  template<class... Ts>
513  class variant;
514 
515  template<typename T>
516  struct is_variant_impl : std::false_type {};
517 
518  template<typename... Ts>
519  struct is_variant_impl<variant<Ts...>> : std::true_type {};
520 
521  template<typename T>
522  struct is_variant : is_variant_impl<std::decay_t<T>> {};
523 
524  template<typename T>
525  inline constexpr bool is_variant_v = is_variant<T>::value;
526 
527  // ill-formed variant, an empty specialization prevents some really bad errors messages on gcc
528  template<class... Ts>
529  requires (
530  (std::is_array_v<Ts> || ...)
531  || (std::is_reference_v<Ts> || ...)
532  || (std::is_void_v<Ts> || ...)
533  || sizeof...(Ts) == 0
534  )
535  class variant<Ts...> {
536  static_assert(sizeof...(Ts) > 0, "A variant cannot be empty.");
537  static_assert(not(std::is_reference_v<Ts> || ...), "A variant cannot contain references, consider using reference wrappers instead.");
538  static_assert(not(std::is_void_v<Ts> || ...), "A variant cannot contains void.");
539  static_assert(not(std::is_array_v<Ts> || ...), "A variant cannot contain a raw array type, consider using std::array instead.");
540  };
541 
542  template<class... Ts>
543  class variant {
544 
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>
618  requires (Index < size && std::is_constructible_v<alternative<Index>, 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)
672  unsafe_get<index_cst>() = elem;
673  else {
674  using type = alternative<index_cst>;
675  constexpr bool do_simple_copy =
676  std::is_nothrow_copy_constructible_v<type>
677  or not std::is_nothrow_move_constructible_v<type>;
678  if constexpr (do_simple_copy)
679  emplace<index_cst>(elem);
680  else {
681  alternative<index_cst> tmp = elem;
682  emplace<index_cst>(PLG_MOV(tmp));
683  }
684  }
685  });
686  return *this;
687  }
688 
689  // move assignment(trivial)
690  constexpr variant& operator=(variant&& o)
691  requires (trivial_move_assign and trivial_move_ctor and trivial_dtor)
692  = default;
693 
694  // move assignment
695  constexpr variant& operator=(variant&& o)
696  noexcept((std::is_nothrow_move_constructible_v<Ts> && ...) && (std::is_nothrow_move_assignable_v<Ts> && ...))
697  requires (has_move_assign && has_move_ctor and not(trivial_move_assign and trivial_move_ctor and trivial_dtor)) {
698  if (this == &o) [[unlikely]]
699  return *this;
700 
701  assign_from(PLG_FWD(o), [&](auto&& elem, auto index_cst) {
702  if (index() == index_cst)
703  unsafe_get<index_cst>() = PLG_MOV(elem);
704  else
705  emplace<index_cst>(PLG_MOV(elem));
706  });
707  return *this;
708  }
709 
710  // generic assignment
711  template<class T>
712  requires detail::has_non_ambiguous_match<T, Ts...>
713  constexpr variant& operator=(T&& t)
714  noexcept(std::is_nothrow_assignable_v<detail::best_overload_match<T&&, Ts...>, T&&>
715  && std::is_nothrow_constructible_v<detail::best_overload_match<T&&, Ts...>, T&&>) {
716  using related_type = detail::best_overload_match<T&&, Ts...>;
717  constexpr auto new_index = index_of<related_type>;
718 
719  if (_current == new_index)
720  unsafe_get<new_index>() = PLG_FWD(t);
721  else {
722  constexpr bool do_simple_emplace =
723  std::is_nothrow_constructible_v<related_type, T>
724  or not std::is_nothrow_move_constructible_v<related_type>;
725 
726  if constexpr (do_simple_emplace)
727  emplace<new_index>(PLG_FWD(t));
728  else {
729  related_type tmp = t;
730  emplace<new_index>(PLG_MOV(tmp));
731  }
732  }
733 
734  return *this;
735  }
736 
737  // ================================== modifiers (20.7.3.5)
738 
739  template<class T, class... Args>
740  requires (std::is_constructible_v<T, Args&&...> && detail::appears_exactly_once<T, Ts...>)
741  constexpr T& emplace(Args&&... args) {
742  return emplace<index_of<T>>(static_cast<Args&&>(args)...);
743  }
744 
745  template<std::size_t Idx, class... Args>
746  requires (Idx < size and std::is_constructible_v<alternative<Idx>, Args&&...> )
747  constexpr auto& emplace(Args&&... args) {
748  return emplace_impl<Idx>(PLG_FWD(args)...);
749  }
750 
751  // emplace with initializer-lists
752  template<std::size_t Idx, class U, class... Args>
753  requires (Idx < size
754  && std::is_constructible_v<alternative<Idx>, std::initializer_list<U>&, Args&&...>)
755  constexpr auto& emplace(std::initializer_list<U> list, Args&&... args) {
756  return emplace_impl<Idx>(list, PLG_FWD(args)...);
757  }
758 
759  template<class T, class U, class... Args>
760  requires (std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>
761  && detail::appears_exactly_once<T, Ts...>)
762  constexpr T& emplace(std::initializer_list<U> list, Args&&... args) {
763  return emplace_impl<index_of<T>>(list, PLG_FWD(args)...);
764  }
765 
766  // ==================================== value status (20.7.3.6)
767 
768  constexpr bool valueless_by_exception() const noexcept {
769  if constexpr (can_be_valueless)
770  return _current == npos;
771  else return false;
772  }
773 
774  constexpr index_type index() const noexcept {
775  return _current;
776  }
777 
778  // =================================== swap (20.7.3.7)
779 
780  constexpr void swap(variant& o)
781  noexcept((std::is_nothrow_move_constructible_v<Ts> && ...)
782  && (detail::swap_trait::template nothrow<Ts> && ...))
783  requires (has_move_ctor && (detail::swap_trait::template able<Ts> && ...)) {
784  if constexpr (can_be_valueless) {
785  // if one is valueless, move the element form the non-empty variant,
786  // reset it, and set it to valueless
787  constexpr auto impl_one_valueless = [](auto&& full, auto& empty) {
788  detail::visit_with_index(PLG_FWD(full), detail::emplace_no_dtor_from_elem<variant&>{empty});
789  full.reset_no_check();
790  full._current = npos;
791  };
792 
793  switch(static_cast<int>(index() == npos) + static_cast<int>(o.index() == npos) * 2) {
794  case 0 :
795  break;
796  case 1 :
797  // "this" is valueless
798  impl_one_valueless(PLG_MOV(o), *this);
799  return;
800  case 2 :
801  // "other" is valueless
802  impl_one_valueless(PLG_MOV(*this), o);
803  return;
804  case 3 :
805  // both are valueless, do nothing
806  return;
807  }
808  }
809 
810  assert(not(valueless_by_exception() && o.valueless_by_exception()));
811 
812  detail::visit_with_index(o, [&o, this](auto&& elem, auto index_cst) {
813  if (index() == index_cst) {
814  using std::swap;
815  swap(unsafe_get<index_cst>(), elem);
816  return;
817  }
818 
819  using idx_t = decltype(index_cst);
820  detail::visit_with_index(*this, [this, &o, &elem](auto&& this_elem, auto this_index) {
821  auto tmp { PLG_MOV(this_elem) };
822 
823  // destruct the element
824  detail::destruct<alternative<this_index>>(this_elem);
825 
826  // ok, we just destroyed the element in this, don't call the dtor again
827  emplace_no_dtor<idx_t::value>(PLG_MOV(elem));
828 
829  // we could refactor this
830  detail::destruct<alternative<idx_t::value>>(elem);
831  o.template emplace_no_dtor<(unsigned)(this_index) >(PLG_MOV(tmp));
832  });
833  });
834  }
835 
836  // +================================== methods for internal use
837  // these methods performs no errors checking at all
838 
839  template<detail::union_index_t Idx>
840  constexpr auto& unsafe_get() & noexcept {
841  static_assert(Idx < size);
842  assert(_current == Idx);
843  return _storage.template get<Idx>();
844  }
845 
846  template<detail::union_index_t Idx>
847  constexpr auto&& unsafe_get() && noexcept {
848  static_assert(Idx < size);
849  assert(_current == Idx);
850  return PLG_MOV(_storage.template get<Idx>());
851  }
852 
853  template<detail::union_index_t Idx>
854  constexpr const auto& unsafe_get() const & noexcept {
855  static_assert(Idx < size);
856  assert(_current == Idx);
857  return _storage.template get<Idx>();
858  }
859 
860  template<detail::union_index_t Idx>
861  constexpr const auto&& unsafe_get() const && noexcept {
862  static_assert(Idx < size);
863  assert(_current == Idx);
864  return PLG_MOV(_storage.template get<Idx>());
865  }
866 
867  private:
868  // assign from another variant
869  template<class Other, class Fn>
870  constexpr void assign_from(Other&& o, Fn&& fn) {
871  if constexpr (can_be_valueless) {
872  if (o.index() == npos) {
873  if (_current != npos) {
874  reset_no_check();
875  _current = npos;
876  }
877  return;
878  }
879  }
880  assert(not o.valueless_by_exception());
881  detail::visit_with_index(PLG_FWD(o), PLG_FWD(fn));
882  }
883 
884  template<unsigned Idx, class... Args>
885  constexpr auto& emplace_impl(Args&&... args) {
886  reset();
887  emplace_no_dtor<Idx>(PLG_FWD(args)...);
888  return unsafe_get<Idx>();
889  }
890 
891  // can be used directly only when the variant is in a known empty state
892  template<unsigned Idx, class... Args>
893  constexpr void emplace_no_dtor(Args&&... args) {
894  using T = alternative<Idx>;
895 
896  if constexpr (not std::is_nothrow_constructible_v<T, Args&&...>)
897  {
898  if constexpr (std::is_nothrow_move_constructible_v<T>)
899  {
900  do_emplace_no_dtor<Idx>(T{PLG_FWD(args)...});
901  }
902  else if constexpr (std::is_nothrow_copy_constructible_v<T>)
903  {
904  T tmp {PLG_FWD(args)...};
905  do_emplace_no_dtor<Idx>(tmp);
906  }
907  else
908  {
909  static_assert(can_be_valueless && Idx == Idx,
910  "Internal error : the possibly valueless branch of emplace was taken despite |can_be_valueless| being false");
911  _current = npos;
912  do_emplace_no_dtor<Idx>(PLG_FWD(args)...);
913  }
914  }
915  else
916  do_emplace_no_dtor<Idx>(PLG_FWD(args)...);
917  }
918 
919  template<unsigned Idx, class... Args>
920  constexpr void do_emplace_no_dtor(Args&&... args) {
921  _current = static_cast<index_type>(Idx);
922 
923  auto* ptr = detail::addressof(unsafe_get<Idx>());
924 
925 #ifdef PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
926  using T = alternative<Idx>;
927  new (const_cast<void*>(ptr)) t(PLG_FWD(args)...);
928 #else
929  std::construct_at(ptr, PLG_FWD(args)...);
930 #endif // PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
931  }
932 
933  // destroy the current elem IFF not valueless
934  constexpr void reset() {
935  if constexpr (can_be_valueless)
936  if (valueless_by_exception()) return;
937  reset_no_check();
938  }
939 
940  // destroy the current element without checking for valueless
941  constexpr void reset_no_check() {
942  assert(index() < size);
943  if constexpr (not trivial_dtor) {
944  detail::visit_with_index(*this, [](auto& elem, auto index_cst) {
945  detail::destruct<alternative<index_cst>>(elem);
946  });
947  }
948  }
949 
950  // construct this from another variant, for constructors only
951  template<class Other>
952  constexpr void construct_from(Other&& o) {
953  if constexpr (can_be_valueless)
954  if (o.valueless_by_exception()) {
955  _current = npos;
956  return;
957  }
958 
959  detail::visit_with_index(PLG_FWD(o), detail::emplace_no_dtor_from_elem<variant&>{*this});
960  }
961 
962  template<class T>
963  friend struct detail::emplace_no_dtor_from_elem;
964 
965  storage _storage;
966  index_type _current;
967  };
968 
969  // ================================= value access (20.7.5)
970 
971  template<class T, class... Ts>
972  constexpr bool holds_alternative(const variant<Ts...>& v) noexcept {
973  static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
974  constexpr auto Index = variant<Ts...>::template index_of<T>;
975  return v.index() == Index;
976  }
977 
978  // ========= get by index
979 
980  template<std::size_t Idx, class... Ts>
981  constexpr auto& get(variant<Ts...>& v) {
982  static_assert(Idx < sizeof...(Ts), "Index exceeds the variant size. ");
983  PLUGIFY_ASSERT(v.index() == Idx, "plg::variant:get(): Bad variant access in get.", bad_variant_access);
984  return (v.template unsafe_get<Idx>());
985  }
986 
987  template<std::size_t Idx, class... Ts>
988  constexpr const auto& get(const variant<Ts...>& v) {
989  return plg::get<Idx>(const_cast<variant<Ts...>&>(v));
990  }
991 
992  template<std::size_t Idx, class... Ts>
993  constexpr auto&& get(variant<Ts...>&& v) {
994  return PLG_MOV(plg::get<Idx>(v));
995  }
996 
997  template<std::size_t Idx, class... Ts>
998  constexpr const auto&& get(const variant<Ts...>&& v) {
999  return PLG_MOV(plg::get<Idx>(v));
1000  }
1001 
1002  // ========= get by type
1003 
1004  template<class T, class... Ts>
1005  constexpr T& get(variant<Ts...>& v) {
1006  return plg::get<variant<Ts...>::template index_of<T> >(v);
1007  }
1008 
1009  template<class T, class... Ts>
1010  constexpr const T& get(const variant<Ts...>& v) {
1011  return plg::get<variant<Ts...>::template index_of<T> >(v);
1012  }
1013 
1014  template<class T, class... Ts>
1015  constexpr T&& get(variant<Ts...>&& v) {
1016  return plg::get<variant<Ts...>::template index_of<T> >(PLG_FWD(v));
1017  }
1018 
1019  template<class T, class... Ts>
1020  constexpr const T&& get(const variant<Ts...>&& v) {
1021  return plg::get<variant<Ts...>::template index_of<T> >(PLG_FWD(v));
1022  }
1023 
1024  // ===== get_if by index
1025 
1026  template<std::size_t Idx, class... Ts>
1027  constexpr const auto* get_if(const variant<Ts...>* v) noexcept {
1028  using rtype = typename variant<Ts...>::template alternative<Idx>*;
1029  if (v == nullptr || v->index() != Idx)
1030  return rtype{nullptr};
1031  else
1032  return detail::addressof(v->template unsafe_get<Idx>());
1033  }
1034 
1035  template<std::size_t Idx, class... Ts>
1036  constexpr auto* get_if(variant<Ts...>* v) noexcept {
1037  using rtype = typename variant<Ts...>::template alternative<Idx>;
1038  return const_cast<rtype*>(
1039  plg::get_if<Idx>(static_cast<const variant<Ts...>*>(v))
1040  );
1041  }
1042 
1043  // ====== get_if by type
1044 
1045  template<class T, class... Ts>
1046  constexpr T* get_if(variant<Ts...>* v) noexcept {
1047  static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
1048  return plg::get_if<variant<Ts...>::template index_of<T> >(v);
1049  }
1050 
1051  template<class T, class... Ts>
1052  constexpr const T* get_if(const variant<Ts...>* v) noexcept {
1053  static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
1054  return plg::get_if<variant<Ts...>::template index_of<T> >(v);
1055  }
1056 
1057  // =============================== visitation (20.7.7)
1058 
1059  template<class Fn, class... Vs>
1060  constexpr decltype(auto) visit(Fn&& fn, Vs&&... vs) {
1061  if constexpr ((std::decay_t<Vs>::can_be_valueless || ...))
1062  PLUGIFY_ASSERT(!(vs.valueless_by_exception() || ...), "plg::variant:visit(): Bad variant access in visit.", bad_variant_access);
1063 
1064  if constexpr (sizeof...(Vs) == 1)
1065  return detail::visit(PLG_FWD(fn), PLG_FWD(vs)...);
1066  else
1067  return detail::multi_visit(PLG_FWD(fn), PLG_FWD(vs)...);
1068  }
1069 
1070  template<class Fn>
1071  constexpr decltype(auto) visit(Fn&& fn) {
1072  return PLG_FWD(fn)();
1073  }
1074 
1075  template<class R, class Fn, class... Vs>
1076  requires (is_variant_v<Vs> && ...)
1077  constexpr R visit(Fn&& fn, Vs&&... vars) {
1078  return static_cast<R>(plg::visit(PLG_FWD(fn), PLG_FWD(vars)...));
1079  }
1080 
1081  // ============================== relational operators (20.7.6)
1082 
1083  template<class... Ts>
1084  requires (detail::has_eq_comp<Ts> && ...)
1085  constexpr bool operator==(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1086  if (v1.index() != v2.index())
1087  return false;
1088  if constexpr (variant<Ts...>::can_be_valueless)
1089  if (v1.valueless_by_exception()) return true;
1090  return detail::visit_with_index(v2, [&v1](auto& elem, auto index) -> bool {
1091  return (v1.template unsafe_get<index>() == elem);
1092  });
1093  }
1094 
1095  template<class... Ts>
1096  constexpr bool operator!=(const variant<Ts...>& v1, const variant<Ts...>& v2)
1097  requires requires { v1 == v2; }
1098  {
1099  return not(v1 == v2);
1100  }
1101 
1102  template<class... Ts>
1103  requires (detail::has_lesser_comp<const Ts&> && ...)
1104  constexpr bool operator<(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1105  if constexpr (variant<Ts...>::can_be_valueless) {
1106  if (v2.valueless_by_exception()) return false;
1107  if (v1.valueless_by_exception()) return true;
1108  }
1109  if (v1.index() == v2.index()) {
1110  return detail::visit_with_index(v1, [&v2](auto& elem, auto index) -> bool {
1111  return (elem < v2.template unsafe_get<index>());
1112  });
1113  }
1114  else
1115  return (v1.index() < v2.index());
1116  }
1117 
1118  template<class... Ts>
1119  constexpr bool operator>(const variant<Ts...>& v1, const variant<Ts...>& v2)
1120  requires requires { v2 < v1; }
1121  {
1122  return v2 < v1;
1123  }
1124 
1125  template<class... Ts>
1126  requires (detail::has_less_or_eq_comp<const Ts&> && ...)
1127  constexpr bool operator<=(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1128  if constexpr (variant<Ts...>::can_be_valueless) {
1129  if (v1.valueless_by_exception()) return true;
1130  if (v2.valueless_by_exception()) return false;
1131  }
1132  if (v1.index() == v2.index()) {
1133  return detail::visit_with_index(v1, [&v2](auto& elem, auto index) -> bool {
1134  return (elem <= v2.template unsafe_get<index>());
1135  });
1136  }
1137  else
1138  return (v1.index() < v2.index());
1139  }
1140 
1141  template<class... Ts>
1142  constexpr bool operator>=(const variant<Ts...>& v1, const variant<Ts...>& v2)
1143  requires requires { v2 <= v1; }
1144  {
1145  return v2 <= v1;
1146  }
1147 
1148  // ===================================== monostate (20.7.8, 20.7.9)
1149 
1150  struct monostate{};
1151  constexpr bool operator==(monostate, monostate) noexcept { return true; }
1152  constexpr bool operator> (monostate, monostate) noexcept { return false; }
1153  constexpr bool operator< (monostate, monostate) noexcept { return false; }
1154  constexpr bool operator<=(monostate, monostate) noexcept { return true; }
1155  constexpr bool operator>=(monostate, monostate) noexcept { return true; }
1156 
1157  // ===================================== specialized algorithms (20.7.10)
1158 
1159  template<class... Ts>
1160  constexpr void swap(variant<Ts...>& a, variant<Ts...>& b)
1161  noexcept(noexcept(a.swap(b)))
1162  requires requires { a.swap(b); }
1163  {
1164  a.swap(b);
1165  }
1166 
1167  // ===================================== helper classes (20.7.4)
1168 
1169  template<class T>
1170  requires is_variant_v<T>
1171  inline constexpr std::size_t variant_size_v = std::decay_t<T>::size;
1173  // not sure why anyone would need this, i'm adding it anyway
1174  template<class T>
1175  requires is_variant_v<T>
1176  struct variant_size : std::integral_constant<std::size_t, variant_size_v<T>> {};
1178  namespace detail {
1179  // ugh, we have to take care of volatile here
1180  template<bool IsVolatile>
1181  struct var_alt_impl {
1182  template<std::size_t Idx, class T>
1183  using type = std::remove_reference_t<decltype(std::declval<T>().template unsafe_get<Idx>())>;
1184  };
1185 
1186  template<>
1187  struct var_alt_impl<true> {
1188  template<std::size_t Idx, class T>
1189  using type = volatile typename var_alt_impl<false>::template type<Idx, std::remove_volatile_t<T>>;
1190  };
1191  }
1192 
1193  template<std::size_t Idx, class T>
1194  requires (Idx < variant_size_v<T>)
1195  using variant_alternative_t = typename detail::var_alt_impl<std::is_volatile_v<T>>::template type<Idx, T>;
1196 
1197  template<std::size_t Idx, class T>
1198  requires is_variant_v<T>
1199  struct variant_alternative {
1200  using type = variant_alternative_t<Idx, T>;
1201  };
1202 
1203  // ===================================== extensions (unsafe_get)
1204 
1205  template<std::size_t Idx, class Var>
1206  requires is_variant_v<Var>
1207  constexpr auto&& unsafe_get(Var&& var) noexcept {
1208  static_assert(Idx < std::decay_t<Var>::size, "Index exceeds the variant size.");
1209  return PLG_FWD(var).template unsafe_get<Idx>();
1210  }
1211 
1212  template<class T, class Var>
1213  requires is_variant_v<Var>
1214  constexpr auto&& unsafe_get(Var&& var) noexcept {
1215  return plg::unsafe_get<std::decay_t<Var>::template index_of<T>>(PLG_FWD(var));
1216  }
1217 
1218 } // namespace plg
1219 
1220 // ====================================== hash support(20.7.12)
1221 #ifndef PLUGIFY_VARIANT_NO_STD_HASH
1222 namespace std {
1223  template<class... Ts>
1224  requires (plg::detail::has_std_hash<Ts> && ...)
1225  struct hash<plg::variant<Ts...>> {
1226  std::size_t operator()(const plg::variant<Ts...>& v) const {
1228  if (v.valueless_by_exception())
1229  return static_cast<std::size_t>(-1);
1230 
1231  return plg::detail::visit_with_index(v, [](auto& elem, auto index_) {
1232  using type = std::remove_cvref_t<decltype(elem)>;
1233  return std::hash<type>{}(elem) + index_;
1234  });
1235  }
1236  };
1237 
1238  template<>
1239  struct hash<plg::monostate> {
1240  constexpr std::size_t operator()(plg::monostate) const noexcept { return static_cast<size_t>(-1); }
1241  };
1242 } // namespace std
1243 #endif // PLUGIFY_VARIANT_NO_STD_HASH
1244 
1245 #undef PLG_FWD
1246 #undef PLG_MOV