#ifdef SWL_CPP_LIBRARY_VARIANT_HPP template constexpr int find_first_true(bool (&&arr)[N]){ for (int k = 0; k < N; ++k) if (arr[k]) return k; return -1; } template inline constexpr bool appears_exactly_once = (static_cast(std::is_same_v) + ...) == 1; // ============= type pack element #if __has_builtin(__type_pack_element) template using type_pack_element = __type_pack_element; #else template struct find_type_i; template <> struct find_type_i<1> { template using f = typename find_type_i<(Idx != 1)>::template f; }; template <> struct find_type_i<0> { template using f = T; }; template using type_pack_element = typename find_type_i<(K != 0)>::template f; #endif // ============= overload match detector. to be used for variant generic assignment template using arr1 = T[1]; template struct overload_frag { using type = A; template requires requires { arr1{std::declval()}; } auto operator()(A, T&&) -> overload_frag; }; template struct make_overload; template struct make_overload, Args...> : overload_frag... { using overload_frag::operator()...; }; template using best_overload_match = typename decltype( make_overload, Ts...>{} ( std::declval(), std::declval() ) )::type; template concept has_non_ambiguous_match = requires { typename best_overload_match; }; // ================================== rel ops template concept convertible = std::is_convertible_v; template concept has_eq_comp = requires (T a, T b) { { a == b } -> convertible; }; template concept has_lesser_comp = requires (T a, T b) { { a < b } -> convertible; }; template concept has_less_or_eq_comp = requires (T a, T b) { { a <= b } -> convertible; }; template struct emplace_no_dtor_from_elem { template constexpr void operator()(T&& elem, auto index_) const { a.template emplace_no_dtor( static_cast(elem) ); } A& a; }; template constexpr void destruct(T& obj){ if constexpr (not std::is_trivially_destructible_v) obj.~E(); } // =============================== variant union types // =================== base variant storage type // this type is used to build a N-ary tree of union. struct dummy_type{ static constexpr int elem_size = 0; }; // used to fill the back of union nodes using union_index_t = unsigned; #define TRAIT(trait) ( std::is_##trait##_v && std::is_##trait##_v ) #define SFM(signature, trait) \ signature = default; \ signature requires (TRAIT(trait) and not TRAIT(trivially_##trait)) {} // given the two members of type A and B of an union X // this create the proper conditionally trivial special members functions #define INJECT_UNION_SFM(X) \ SFM(constexpr X (const X &), copy_constructible) \ SFM(constexpr X (X&&), move_constructible) \ SFM(constexpr X& operator=(const X&), copy_assignable) \ SFM(constexpr X& operator=(X&&), move_assignable) \ SFM(constexpr ~X(), destructible) template struct node_trait; template <> struct node_trait { template static constexpr auto elem_size = not( std::is_same_v ) ? 2 : 1; template static constexpr char ctor_branch = 0; }; template <> struct node_trait { template static constexpr auto elem_size = A::elem_size + B::elem_size; template static constexpr char ctor_branch = (Index < A::elem_size) ? 1 : 2; }; template struct union_node { union { A a; B b; }; static constexpr auto elem_size = node_trait::template elem_size; constexpr union_node() = default; template requires (node_trait::template ctor_branch == 1) constexpr union_node(in_place_index_t, Args&&... args) : a{ in_place_index, static_cast(args)... } {} template requires (node_trait::template ctor_branch == 2) constexpr union_node(in_place_index_t, Args&&... args) : b{ in_place_index, static_cast(args)... } {} template requires (IsLeaf) constexpr union_node(in_place_index_t<0>, Args&&... args) : a{static_cast(args)...} {} template requires (IsLeaf) constexpr union_node(in_place_index_t<1>, Args&&... args) : b{static_cast(args)...} {} constexpr union_node(dummy_type) requires (std::is_same_v) : b{} {} template constexpr auto& get() { if constexpr (IsLeaf) { if constexpr ( Index == 0 ) return a; else return b; } else { if constexpr ( Index < A::elem_size ) return a.template get(); else return b.template get(); } } INJECT_UNION_SFM(union_node) }; #undef INJECT_UNION_SFM #undef SFM #undef TRAIT // =================== algorithm to build the tree of unions // take a sequence of types and perform an order preserving fold until only one type is left // the first parameter is the numbers of types remaining for the current pass constexpr unsigned char pick_next(unsigned remaining){ return remaining >= 2 ? 2 : remaining; } template struct make_tree; template struct make_tree<2, 1, IsFirstPass> { template using f = typename make_tree < pick_next(Remaining - 2), sizeof...(Ts) != 0, IsFirstPass > ::template f < Remaining - 2, Ts..., union_node >; }; // only one type left, stop template struct make_tree<0, 0, F> { template using f = A; }; // end of one pass, restart template struct make_tree<0, 1, IsFirstPass> { template using f = typename make_tree < pick_next(sizeof...(Ts)), (sizeof...(Ts) != 1), false // <- both first pass and tail call recurse into a tail call > ::template f; }; // one odd type left in the pass, put it at the back to preserve the order template <> struct make_tree<1, 1, false> { template using f = typename make_tree<0, sizeof...(Ts) != 0, false> ::template f<0, Ts..., A>; }; // one odd type left in the first pass, wrap it in an union template <> struct make_tree<1, 1, true> { template using f = typename make_tree<0, sizeof...(Ts) != 0, false> ::template f<0, Ts..., union_node>; }; template using make_tree_union = typename make_tree::template f; // ============================================================ // Ts... must be sorted in ascending size template using smallest_suitable_integer_type = type_pack_element<(static_cast(Num > std::numeric_limits::max()) + ...), Ts... >; // why do we need this again? i think something to do with GCC? namespace swap_trait { using std::swap; template concept able = requires (A a, A b) { swap(a, b); }; template inline constexpr bool nothrow = noexcept( swap(std::declval(), std::declval()) ); } #ifndef SWL_VARIANT_NO_STD_HASH template inline constexpr bool has_std_hash = requires (T t) { std::size_t( ::std::hash< std::remove_cvref_t >{}(t) ); }; #endif template inline constexpr T* addressof( T& obj ) noexcept { #if defined(__GNUC__) || defined( __clang__ ) return __builtin_addressof(obj); #elif defined (SWL_VARIANT_NO_CONSTEXPR_EMPLACE) // if & is overloaded, use the ugly version if constexpr ( requires { obj.operator&(); } ) return reinterpret_cast (&const_cast(reinterpret_cast(obj))); else return &obj; #else return std::address_of(obj); #endif } #endif // eof