336 lines
8.8 KiB
C++
336 lines
8.8 KiB
C++
#ifdef SWL_CPP_LIBRARY_VARIANT_HPP
|
|
|
|
template <int N>
|
|
constexpr int find_first_true(bool (&&arr)[N]){
|
|
for (int k = 0; k < N; ++k)
|
|
if (arr[k])
|
|
return k;
|
|
return -1;
|
|
}
|
|
|
|
template <class T, class... Ts>
|
|
inline constexpr bool appears_exactly_once = (static_cast<unsigned short>(std::is_same_v<T, Ts>) + ...) == 1;
|
|
|
|
// ============= type pack element
|
|
|
|
#if __has_builtin(__type_pack_element)
|
|
|
|
template <std::size_t K, class... Ts>
|
|
using type_pack_element = __type_pack_element<K, Ts...>;
|
|
|
|
#else
|
|
|
|
template <unsigned char = 1>
|
|
struct find_type_i;
|
|
|
|
template <>
|
|
struct find_type_i<1> {
|
|
template <std::size_t Idx, class T, class... Ts>
|
|
using f = typename find_type_i<(Idx != 1)>::template f<Idx - 1, Ts...>;
|
|
};
|
|
|
|
template <>
|
|
struct find_type_i<0> {
|
|
template <std::size_t, class T, class... Ts>
|
|
using f = T;
|
|
};
|
|
|
|
template <std::size_t K, class... Ts>
|
|
using type_pack_element = typename find_type_i<(K != 0)>::template f<K, Ts...>;
|
|
|
|
#endif
|
|
|
|
// ============= overload match detector. to be used for variant generic assignment
|
|
|
|
template <class T>
|
|
using arr1 = T[1];
|
|
|
|
template <std::size_t N, class A>
|
|
struct overload_frag {
|
|
using type = A;
|
|
template <class T>
|
|
requires requires { arr1<A>{std::declval<T>()}; }
|
|
auto operator()(A, T&&) -> overload_frag<N, A>;
|
|
};
|
|
|
|
template <class Seq, class... Args>
|
|
struct make_overload;
|
|
|
|
template <std::size_t... Idx, class... Args>
|
|
struct make_overload<std::integer_sequence<std::size_t, Idx...>, Args...>
|
|
: overload_frag<Idx, Args>... {
|
|
using overload_frag<Idx, Args>::operator()...;
|
|
};
|
|
|
|
template <class T, class... Ts>
|
|
using best_overload_match = typename decltype(
|
|
make_overload<std::make_index_sequence<sizeof...(Ts)>, Ts...>{}
|
|
( std::declval<T>(), std::declval<T>() )
|
|
)::type;
|
|
|
|
template <class T, class... Ts>
|
|
concept has_non_ambiguous_match =
|
|
requires { typename best_overload_match<T, Ts...>; };
|
|
|
|
// ================================== rel ops
|
|
|
|
template <class From, class To>
|
|
concept convertible = std::is_convertible_v<From, To>;
|
|
|
|
template <class T>
|
|
concept has_eq_comp = requires (T a, T b) {
|
|
{ a == b } -> convertible<bool>;
|
|
};
|
|
|
|
template <class T>
|
|
concept has_lesser_comp = requires (T a, T b) {
|
|
{ a < b } -> convertible<bool>;
|
|
};
|
|
|
|
template <class T>
|
|
concept has_less_or_eq_comp = requires (T a, T b) {
|
|
{ a <= b } -> convertible<bool>;
|
|
};
|
|
|
|
template <class A>
|
|
struct emplace_no_dtor_from_elem {
|
|
template <class T>
|
|
constexpr void operator()(T&& elem, auto index_) const {
|
|
a.template emplace_no_dtor<index_>( static_cast<T&&>(elem) );
|
|
}
|
|
A& a;
|
|
};
|
|
|
|
template <class E, class T>
|
|
constexpr void destruct(T& obj){
|
|
if constexpr (not std::is_trivially_destructible_v<E>)
|
|
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<A> && std::is_##trait##_v<B> )
|
|
|
|
#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 <bool IsLeaf>
|
|
struct node_trait;
|
|
|
|
template <>
|
|
struct node_trait<true> {
|
|
|
|
template <class A, class B>
|
|
static constexpr auto elem_size = not( std::is_same_v<B, dummy_type> ) ? 2 : 1;
|
|
|
|
template <std::size_t, class>
|
|
static constexpr char ctor_branch = 0;
|
|
};
|
|
|
|
template <>
|
|
struct node_trait<false> {
|
|
template <class A, class B>
|
|
static constexpr auto elem_size = A::elem_size + B::elem_size;
|
|
|
|
template <std::size_t Index, class A>
|
|
static constexpr char ctor_branch = (Index < A::elem_size) ? 1 : 2;
|
|
};
|
|
|
|
template <bool IsLeaf, class A, class B>
|
|
struct union_node {
|
|
|
|
union {
|
|
A a;
|
|
B b;
|
|
};
|
|
|
|
static constexpr auto elem_size = node_trait<IsLeaf>::template elem_size<A, B>;
|
|
|
|
constexpr union_node() = default;
|
|
|
|
template <std::size_t Index, class... Args>
|
|
requires (node_trait<IsLeaf>::template ctor_branch<Index, A> == 1)
|
|
constexpr union_node(in_place_index_t<Index>, Args&&... args)
|
|
: a{ in_place_index<Index>, static_cast<Args&&>(args)... }
|
|
{}
|
|
|
|
template <std::size_t Index, class... Args>
|
|
requires (node_trait<IsLeaf>::template ctor_branch<Index, A> == 2)
|
|
constexpr union_node(in_place_index_t<Index>, Args&&... args)
|
|
: b{ in_place_index<Index - A::elem_size>, static_cast<Args&&>(args)... }
|
|
{}
|
|
|
|
template <class... Args>
|
|
requires (IsLeaf)
|
|
constexpr union_node(in_place_index_t<0>, Args&&... args)
|
|
: a{static_cast<Args&&>(args)...}
|
|
{}
|
|
|
|
template <class... Args>
|
|
requires (IsLeaf)
|
|
constexpr union_node(in_place_index_t<1>, Args&&... args)
|
|
: b{static_cast<Args&&>(args)...}
|
|
{}
|
|
|
|
constexpr union_node(dummy_type)
|
|
requires (std::is_same_v<dummy_type, B>)
|
|
: b{}
|
|
{}
|
|
|
|
template <union_index_t Index>
|
|
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<Index>();
|
|
else
|
|
return b.template get<Index - A::elem_size>();
|
|
}
|
|
}
|
|
|
|
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 <unsigned char Pick, unsigned char GoOn, bool FirstPass>
|
|
struct make_tree;
|
|
|
|
template <bool IsFirstPass>
|
|
struct make_tree<2, 1, IsFirstPass> {
|
|
template <unsigned Remaining, class A, class B, class... Ts>
|
|
using f = typename make_tree
|
|
<
|
|
pick_next(Remaining - 2),
|
|
sizeof...(Ts) != 0,
|
|
IsFirstPass
|
|
>
|
|
::template f
|
|
<
|
|
Remaining - 2,
|
|
Ts...,
|
|
union_node<IsFirstPass, A, B>
|
|
>;
|
|
};
|
|
|
|
// only one type left, stop
|
|
template <bool F>
|
|
struct make_tree<0, 0, F> {
|
|
template <unsigned, class A>
|
|
using f = A;
|
|
};
|
|
|
|
// end of one pass, restart
|
|
template <bool IsFirstPass>
|
|
struct make_tree<0, 1, IsFirstPass> {
|
|
template <unsigned Remaining, class... Ts>
|
|
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<sizeof...(Ts), Ts...>;
|
|
};
|
|
|
|
// one odd type left in the pass, put it at the back to preserve the order
|
|
template <>
|
|
struct make_tree<1, 1, false> {
|
|
template <unsigned Remaining, class A, class... Ts>
|
|
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 <unsigned, class A, class... Ts>
|
|
using f = typename make_tree<0, sizeof...(Ts) != 0, false>
|
|
::template f<0, Ts..., union_node<true, A, dummy_type>>;
|
|
};
|
|
|
|
template <class... Ts>
|
|
using make_tree_union = typename
|
|
make_tree<pick_next(sizeof...(Ts)), 1, true>::template f<sizeof...(Ts), Ts...>;
|
|
|
|
// ============================================================
|
|
|
|
// Ts... must be sorted in ascending size
|
|
template <std::size_t Num, class... Ts>
|
|
using smallest_suitable_integer_type =
|
|
type_pack_element<(static_cast<unsigned char>(Num > std::numeric_limits<Ts>::max()) + ...),
|
|
Ts...
|
|
>;
|
|
|
|
// why do we need this again? i think something to do with GCC?
|
|
namespace swap_trait {
|
|
using std::swap;
|
|
|
|
template <class A>
|
|
concept able = requires (A a, A b) { swap(a, b); };
|
|
|
|
template <class A>
|
|
inline constexpr bool nothrow = noexcept( swap(std::declval<A&>(), std::declval<A&>()) );
|
|
}
|
|
|
|
#ifndef SWL_VARIANT_NO_STD_HASH
|
|
template <class T>
|
|
inline constexpr bool has_std_hash = requires (T t) {
|
|
std::size_t( ::std::hash< std::remove_cvref_t<T> >{}(t) );
|
|
};
|
|
#endif
|
|
|
|
template <class T>
|
|
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<T*>
|
|
(&const_cast<char&>(reinterpret_cast<const volatile char&>(obj)));
|
|
else
|
|
return &obj;
|
|
#else
|
|
return std::address_of(obj);
|
|
#endif
|
|
}
|
|
|
|
#endif // eof
|
|
|