Created
September 17, 2024 18:24
-
-
Save ericniebler/cba16cb75d27631327b7235ef4c1e9f9 to your computer and use it in GitHub Desktop.
meta-programming library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2021-2024 NVIDIA Corporation | |
* | |
* Licensed under the Apache License Version 2.0 with LLVM Exceptions | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* https://llvm.org/LICENSE.txt | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
#pragma once | |
#include <cstddef> | |
#include <cassert> | |
#include <compare> | |
#include <exception> | |
#include <type_traits> | |
#include <utility> | |
#define LBRACKET [[ | |
#define RBRACKET ]] | |
#if defined(__clang__) | |
# define __always_inline__ gnu::always_inline | |
#elif defined(__GNUC__) | |
# define __always_inline__ gnu::always_inline | |
#elif defined(_MSC_VER) | |
# define __always_inline__ RBRACKET __forceinline LBRACKET | |
#else | |
# define __always_inline__ | |
#endif | |
#if defined(_MSC_VER) | |
# define __ebo__ RBRACKET __declspec(empty_bases) LBRACKET | |
#else | |
# define __ebo__ | |
#endif | |
#if defined(__GNUC__) | |
# define META_HAS_BUILTIN __has_builtin | |
#elif defined(_MSC_VER) | |
# define META_HAS_BUILTIN(X) 0 | |
#else | |
# define META_HAS_BUILTIN(X) 0 | |
#endif | |
namespace meta { | |
struct immovable { | |
immovable() = default; | |
immovable(immovable&&) = delete; | |
}; | |
struct ignore { | |
template <class... Ts> | |
constexpr ignore(Ts&&...) noexcept {} | |
}; | |
template <size_t> | |
using ignore_t = ignore; | |
template <class T, template <class...> class C> | |
inline constexpr bool is_instance_of_impl = false; | |
template <class... T, template <class...> class C> | |
inline constexpr bool is_instance_of_impl<C<T...>, C> = true; | |
template <class T, template <class...> class C> | |
concept is_instance_of = is_instance_of_impl<T, C>; | |
template <class T, template <class...> class C> | |
concept is_not_instance_of = !is_instance_of_impl<T, C>; | |
template <class T, class... Ts> | |
concept one_of = (std::same_as<T, Ts> ||...); | |
template <class...> | |
concept always_true = true; | |
////////////////////////////////////////////////////////////////////////////////////////////////// | |
// copy_cvref_t: For copying cvref from one type to another | |
struct cp { | |
template <class Tp> | |
using call = Tp; | |
}; | |
struct cpc { | |
template <class Tp> | |
using call = const Tp; | |
}; | |
struct cplr { | |
template <class Tp> | |
using call = Tp&; | |
}; | |
struct cprr { | |
template <class Tp> | |
using call = Tp&&; | |
}; | |
struct cpclr { | |
template <class Tp> | |
using call = const Tp&; | |
}; | |
struct cpcrr { | |
template <class Tp> | |
using call = const Tp&&; | |
}; | |
template <class> | |
extern cp cpcvr; | |
template <class Tp> | |
extern cpc cpcvr<const Tp>; | |
template <class Tp> | |
extern cplr cpcvr<Tp&>; | |
template <class Tp> | |
extern cprr cpcvr<Tp&&>; | |
template <class Tp> | |
extern cpclr cpcvr<const Tp&>; | |
template <class Tp> | |
extern cpcrr cpcvr<const Tp&&>; | |
template <class Tp> | |
using copy_cvref_fn = decltype(cpcvr<Tp>); | |
template <class From, class To> | |
using copy_cvref_t = typename copy_cvref_fn<From>::template call<To>; | |
template <class Tp> | |
using t = typename Tp::t; | |
template <class Ty> | |
struct type_identity { | |
using t = Ty; | |
}; | |
template <class...> | |
inline constexpr bool mnever = false; | |
template <auto Value> | |
using mtypeof = decltype(Value); | |
template <class...> | |
struct types; | |
template <class Tp> | |
using midentity = Tp; | |
template <auto Np> | |
struct mconstant { | |
using type = mconstant; | |
using value_type = mtypeof<Np>; | |
static constexpr auto value = Np; | |
constexpr operator value_type() const noexcept { | |
return value; | |
} | |
constexpr auto operator()() const noexcept -> value_type { | |
return value; | |
} | |
}; | |
// Because of nvc++ nvbugs#4679848, we can't make mbool a simple alias for mconstant, | |
// and because of nvc++ nvbugs#4668709 it can't be a simple alias for std::bool_constant, | |
// either. :-( | |
// template <bool Bp> | |
// using mbool = mconstant<Bp>; | |
template <bool Bp> | |
struct mbool : std::bool_constant<Bp> { }; | |
using mtrue = mbool<true>; | |
using mfalse = mbool<false>; | |
// nvbugs#4679848 and nvbugs#4668709 also preclude mconstant from representing a compile-time | |
// size_t. | |
enum class muchar : unsigned char { | |
}; | |
#if defined(_MSC_VER) | |
template <std::size_t Np> | |
using msize_t = mconstant<Np>; | |
#else | |
template <std::size_t Np> | |
using msize_t = muchar (*)[Np + 1]; // +1 to avoid zero-size array | |
#endif | |
template <class Tp, class Up> | |
using mfirst = Tp; | |
template <class Tp, class Up> | |
using msecond = Up; | |
template <class...> | |
struct undefined; | |
template <class Tp> | |
extern const undefined<Tp> v; | |
template <class Tp> | |
requires always_true<mtypeof<Tp::value>> | |
inline constexpr auto v<Tp> = Tp::value; | |
// These specializations exist because instantiating a variable template is cheaper than | |
// instantiating a class template. | |
template <class Tp, class Up> | |
inline constexpr bool v<std::is_same<Tp, Up>> = false; | |
template <class Tp> | |
inline constexpr bool v<std::is_same<Tp, Tp>> = true; | |
template <class Tp, Tp Ip> | |
inline constexpr Tp v<std::integral_constant<Tp, Ip>> = Ip; | |
template <auto Np> | |
inline constexpr auto v<mconstant<Np>> = Np; | |
template <std::size_t Np> | |
inline constexpr std::size_t v<muchar (*)[Np]> = Np - 1; // see definition of msize_t | |
namespace pack { | |
template <std::size_t... Is> | |
struct t; | |
} // namespace pack | |
template <std::size_t... Is> | |
using indices = pack::t<Is...> *; | |
#if defined(_MSC_VER) | |
namespace pack { | |
template <class Ty, Ty... Is> | |
struct idx; | |
template <class> | |
extern int mkidx; | |
template <std::size_t... Is> | |
extern indices<Is...> mkidx<idx<std::size_t, Is...>>; | |
} // namespace pack | |
template <std::size_t Np> | |
using make_indices = // | |
decltype(pack::mkidx<__make_integer_seq<pack::idx, std::size_t, Np>>); | |
#elif META_HAS_BUILTIN(__make_integer_seq) | |
namespace pack { | |
template <class Ty, Ty... Is> | |
using idx = indices<Is...>; | |
} // namespace pack | |
template <std::size_t Np> | |
using make_indices = __make_integer_seq<pack::idx, std::size_t, Np>; | |
#elif META_HAS_BUILTIN(__integer_pack) | |
namespace pack { | |
template <std::size_t Np> | |
extern indices<__integer_pack(Np)...> make_indices; | |
} // namespace pack | |
template <std::size_t Np> | |
using make_indices = decltype(pack::make_indices<Np>); | |
#else | |
namespace pack { | |
template <std::size_t... Is> | |
auto mk_indices(indices<0, Is...>) -> indices<Is...>; | |
template <std::size_t Np, class = char[Np], std::size_t... Is> | |
auto mk_indices(indices<Np, Is...>) | |
-> decltype(mk_indices(indices<Np - 1, 0, (Is + 1)...>{})); | |
} // namespace pack | |
template <std::size_t Np> | |
using make_indices = decltype(pack::mk_indices(indices<Np>{})); | |
#endif | |
template <class... Ts> | |
using indices_for = make_indices<sizeof...(Ts)>; | |
// META_PRAGMA_PUSH() | |
// META_PRAGMA_IGNORE_MSVC(4293) | |
constexpr std::size_t mpow2(std::size_t size) noexcept { | |
--size; | |
size |= size >> 1; | |
size |= size >> 2; | |
size |= size >> 4; | |
size |= size >> 8; | |
if constexpr (sizeof(size) >= 4) | |
size |= size >> 16; | |
if constexpr (sizeof(size) >= 8) | |
size |= size >> 32; | |
return ++size; | |
} | |
// META_PRAGMA_POP() | |
template <std::size_t Len> | |
struct mstring { | |
#if defined(__NVCOMPILER) | |
template <std::size_t Ny, std::size_t... Is> | |
constexpr mstring(const char (&str)[Ny], indices<Is...>) noexcept | |
: what_{(Is < Ny ? str[Is] : '\0')...} { | |
} | |
template <std::size_t Ny> | |
constexpr mstring(const char (&str)[Ny], int = 0) noexcept | |
: mstring{str, make_indices<Len>{}} { | |
} | |
#else | |
template <std::size_t Ny> | |
constexpr mstring(const char (&str)[Ny], int = 0) noexcept | |
: what_{} { | |
for (auto i = 0ull; char ch: str) { | |
what_[i++] = ch; | |
} | |
} | |
#endif | |
static constexpr auto length() noexcept -> std::size_t { | |
return Len; | |
} | |
constexpr auto operator==(const mstring &) const noexcept -> bool = default; | |
template <std::size_t OtherLen> | |
constexpr auto operator==(const mstring<OtherLen> &) const noexcept -> bool { | |
return false; | |
} | |
#if !defined(__NVCOMPILER) | |
constexpr auto operator<=>(const mstring &) const noexcept -> std::strong_ordering = default; | |
#endif | |
template <std::size_t OtherLen> | |
constexpr auto | |
operator<=>(const mstring<OtherLen> &other) const noexcept -> std::strong_ordering { | |
constexpr std::size_t len = Len < OtherLen ? Len : OtherLen; | |
for (std::size_t i = 0; i < len; ++i) { | |
auto cmp = (what_[i] <=> other.what_[i]); | |
if (cmp != 0) { | |
return cmp; | |
} | |
} | |
if constexpr (Len == OtherLen) { | |
return std::strong_ordering::equal; | |
} | |
return (Len < OtherLen) ? std::strong_ordering::less : std::strong_ordering::greater; | |
} | |
char what_[Len]; | |
}; | |
template <std::size_t Len> | |
mstring(const char (&str)[Len]) -> mstring<Len>; | |
template <std::size_t Len> | |
mstring(const char (&str)[Len], int) -> mstring<mpow2(Len)>; | |
// Use a standard user-defined string literal template | |
template <mstring Str> | |
constexpr auto operator""_mstr() noexcept -> mtypeof<Str> { | |
return Str; | |
} | |
template <class T> | |
constexpr auto mnameof() noexcept { | |
#if defined(_MSC_VER) | |
return mstring{__FUNCSIG__, 0}; | |
#else | |
return mstring{__PRETTY_FUNCTION__, 0}; | |
#endif | |
} | |
using msuccess = int; | |
template <class What, class... With> | |
struct WARNING_ { }; | |
template <class What, class... With> | |
struct ERROR_ { | |
auto operator,(msuccess) const noexcept -> ERROR_; | |
}; | |
template <mstring... What> | |
struct WHAT_ { }; | |
template <class What, class... With> | |
using mexception = ERROR_<What, With...>; | |
template <class> | |
extern msuccess ok_v; | |
template <class What, class... With> | |
extern ERROR_<What, With...> ok_v<mexception<What, With...>>; | |
template <class Ty> | |
using ok_t = decltype(ok_v<Ty>); | |
template <class... Ts> | |
using disp = decltype((msuccess(), ..., ok_t<Ts>())); | |
template <class Arg> | |
concept ok = std::is_same_v<ok_t<Arg>, msuccess>; | |
template <class Arg> | |
concept merror = !std::is_same_v<ok_t<Arg>, msuccess>; | |
template <class... Args> | |
concept Ok = (std::is_same_v<ok_t<Args>, msuccess> && ...); | |
template <bool ArgsOK, bool FnOK = true> | |
struct i; | |
#if defined(__NVCOMPILER) | |
// Most compilers memoize alias template specializations, but | |
// nvc++ does not. So we memoize the type computations by | |
// indirecting through a class template specialization. | |
template <template <class...> class Fn, class... Args> | |
using meval = // | |
typename i<Ok<Args...>> // | |
::template g<Fn, Args...>; // | |
template <template <class...> class Fn, class... Args> | |
struct meval_ { }; | |
template <template <class...> class Fn, class... Args> | |
requires typename<meval<Fn, Args...>> | |
struct meval_<Fn, Args...> { | |
using t = meval<Fn, Args...>; | |
}; | |
template <template <class...> class Fn, class... Args> | |
using meval = t<meval_<Fn, Args...>>; | |
template <class Fn, class... Args> | |
using minvoke = // | |
typename i<Ok<Args...>, Ok<Fn>> // | |
::template call<Fn> // | |
::template call<Args...>; // | |
template <class Fn, class... Args> | |
struct minvoke_ { }; | |
template <class Fn, class... Args> | |
requires typename<minvoke<Fn, Args...>> | |
struct minvoke_<Fn, Args...> { | |
using t = minvoke<Fn, Args...>; | |
}; | |
template <class Fn, class... Args> | |
using minvoke = t<minvoke_<Fn, Args...>>; | |
#else | |
template <template <class...> class Fn, class... Args> | |
using meval = // | |
typename i<Ok<Args...>> // | |
::template g<Fn, Args...>; // | |
template <class Fn, class... Args> | |
using minvoke = // | |
typename i<Ok<Args...>, Ok<Fn>> // | |
::template call<Fn> // | |
::template call<Args...>; // | |
#endif | |
template <class Fn, class... Args> | |
using mcall = typename Fn::template call<Args...>; | |
struct disp_q { | |
template <class... Args> | |
using call = disp<Args...>; | |
}; | |
template <> | |
struct i<true, true> { | |
template <template <class...> class Fn, class... Args> | |
using g = Fn<Args...>; | |
template <class Fn> | |
using call = Fn; | |
}; | |
template <> | |
struct i<false, true> { | |
template <template <class...> class, class... Args> | |
using g = disp<Args...>; | |
template <class> | |
using call = disp_q; | |
}; | |
template <bool ArgsOK> | |
struct i<ArgsOK, false> { | |
template <class Fn> | |
using call = Fn; | |
}; | |
template <template <class...> class Fn> | |
struct q { | |
template <class... Args> | |
using call = typename i<Ok<Args...>>::template g<Fn, Args...>; | |
}; | |
template <template <class...> class Fn> | |
struct qq { | |
template <class... Args> | |
using call = Fn<Args...>; | |
}; | |
template <template <class> class Fn> | |
struct q1 { | |
template <class Ty> | |
using call = Fn<Ty>; | |
}; | |
template <template <class, class> class Fn> | |
struct q2 { | |
template <class Ty, class Uy> | |
using call = Fn<Ty, Uy>; | |
}; | |
template <template <class...> class Fn> | |
using mtry_q = q<Fn>; | |
template <class Fn> | |
struct mtry : mtry_q<Fn::template call> { }; | |
template <template <class...> class Fn, class... Front> | |
struct mbind_front_q { | |
template <class... Args> | |
using call = meval<Fn, Front..., Args...>; | |
}; | |
template <class Fn, class... Front> | |
using mbind_front = mbind_front_q<Fn::template call, Front...>; | |
template <template <class...> class Fn, class... Back> | |
struct mbind_back_q { | |
template <class... Args> | |
using call = meval<Fn, Args..., Back...>; | |
}; | |
template <class Fn, class... Back> | |
using mbind_back = mbind_back_q<Fn::template call, Back...>; | |
template <template <class...> class Tp, class... Args> | |
concept mvalid = requires { typename meval<Tp, Args...>; }; | |
template <class Fn, class... Args> | |
concept minvocable = mvalid<Fn::template call, Args...>; | |
template <template <class...> class Tp, class... Args> | |
concept msucceeds = mvalid<Tp, Args...> && ok<meval<Tp, Args...>>; | |
template <class Fn, class... Args> | |
concept minvocable_succeeds = minvocable<Fn, Args...> && ok<minvoke<Fn, Args...>>; | |
template <class Fn, class... Args> | |
struct minvoke_force_ { | |
using t = minvoke<Fn, Args...>; | |
}; | |
template <class Fn, class... Args> | |
using minvoke_force = t<minvoke_force_<Fn, Args...>>; | |
template <class Fn, class... Args> | |
struct mdefer_ { }; | |
template <class Fn, class... Args> | |
requires minvocable<Fn, Args...> | |
struct mdefer_<Fn, Args...> { | |
using t = minvoke<Fn, Args...>; | |
}; | |
template <class Fn, class... Args> | |
struct mdefer : mdefer_<Fn, Args...> { }; | |
template <class Fn, class... Args> | |
using mmemoize = t<mdefer<Fn, Args...>>; | |
template <template <class...> class Fn, class... Args> | |
using mmemoize_q = mmemoize<q<Fn>, Args...>; | |
struct if_ { | |
template <bool> | |
struct impl { | |
template <class True, class...> | |
using call = True; | |
}; | |
template <class Pred, class True, class... False> | |
using call = minvoke<impl<static_cast<bool>(v<Pred>)>, True, False...>; | |
}; | |
template <> | |
struct if_::impl<false> { | |
template <class, class False> | |
using call = False; | |
}; | |
template <class Pred, class True = void, class... False> | |
requires(sizeof...(False) <= 1) | |
using mif = minvoke<if_, Pred, True, False...>; | |
template <bool Pred, class True = void, class... False> | |
requires(sizeof...(False) <= 1) | |
using mif_c = minvoke<if_::impl<Pred>, True, False...>; | |
template <class Pred, class True, class False, class... Args> | |
using minvoke_if = minvoke<mif<Pred, True, False>, Args...>; | |
template <bool Pred, class True, class False, class... Args> | |
using minvoke_if_c = minvoke<mif_c<Pred, True, False>, Args...>; | |
template <class Tp> | |
struct mconst { | |
template <class...> | |
using call = Tp; | |
}; | |
inline constexpr mstring mbad_substitution = | |
"The specified meta-function could not be evaluated with the types provided."_mstr; | |
template <mstring Diagnostic = mbad_substitution> | |
struct BAD_SUBSTITUTION_ { }; | |
template <class... Args> | |
struct WITH_TYPES_; | |
template <template <class...> class Fun> | |
struct WITH_META_FUNCTION_T_ { | |
template <class... Args> | |
using call = mexception<BAD_SUBSTITUTION_<>, WITH_META_FUNCTION_T_, WITH_TYPES_<Args...>>; | |
}; | |
template <class Fun> | |
struct WITH_META_FUNCTION_ { | |
template <class... Args> | |
using call = mexception<BAD_SUBSTITUTION_<>, WITH_META_FUNCTION_, WITH_TYPES_<Args...>>; | |
}; | |
template <template <class...> class Try, class Catch> | |
struct mtry_catch_q { | |
template <class... Args> | |
using call = minvoke<mif_c<mvalid<Try, Args...>, q<Try>, Catch>, Args...>; | |
}; | |
template <class Try, class Catch> | |
struct mtry_catch { | |
template <class... Args> | |
using call = minvoke<mif_c<minvocable<Try, Args...>, Try, Catch>, Args...>; | |
}; | |
template <class Fn, class Default> | |
using with_default = mtry_catch<Fn, mconst<Default>>; | |
template <template <class...> class Fn, class Default> | |
using with_default_q = mtry_catch_q<Fn, mconst<Default>>; | |
template <class Fn, class Default, class... Args> | |
using minvoke_or = minvoke<with_default<Fn, Default>, Args...>; | |
template <template <class...> class Fn, class Default, class... Args> | |
using meval_or = minvoke<with_default_q<Fn, Default>, Args...>; | |
template <template <class...> class Fn> | |
struct mtry_eval_ { | |
template <class... Args> | |
using call = meval<Fn, Args...>; | |
}; | |
template <template <class...> class Fn, class... Args> | |
using mtry_eval = | |
minvoke<mtry_catch<mtry_eval_<Fn>, WITH_META_FUNCTION_T_<Fn>>, Args...>; | |
template <class Fn, class... Args> | |
using mtry_invoke = minvoke<mtry_catch<Fn, WITH_META_FUNCTION_<Fn>>, Args...>; | |
template <class Ty, class... Default> | |
using msuccess_or_t = mif_c<ok<Ty>, Ty, Default...>; | |
template <class Ty, class... Default> | |
using merror_or_t = mif_c<merror<Ty>, Ty, Default...>; | |
template <class Fn, class Continuation = q<types>> | |
struct mtransform { | |
template <class... Args> | |
using call = minvoke<Continuation, minvoke<Fn, Args>...>; | |
}; | |
template <bool> | |
struct mfold_right_ { | |
template <class Fn, class State, class Head, class... Tail> | |
using call = | |
minvoke<mfold_right_<sizeof...(Tail) == 0>, Fn, minvoke<Fn, State, Head>, Tail...>; | |
}; | |
template <> | |
struct mfold_right_<true> { // empty pack | |
template <class Fn, class State, class...> | |
using call = State; | |
}; | |
template <class Init, class Fn> | |
struct mfold_right { | |
template <class... Args> | |
using call = minvoke<mfold_right_<sizeof...(Args) == 0>, Fn, Init, Args...>; | |
}; | |
template <bool> | |
struct mfold_left_ { | |
template <class Fn, class State, class Head, class... Tail> | |
using call = | |
minvoke<Fn, mcall<mfold_left_<sizeof...(Tail) == 0>, Fn, State, Tail...>, Head>; | |
}; | |
template <> | |
struct mfold_left_<true> { // empty pack | |
template <class Fn, class State, class...> | |
using call = State; | |
}; | |
template <class Init, class Fn> | |
struct mfold_left { | |
template <class... Args> | |
using call = minvoke<mfold_left_<sizeof...(Args) == 0>, Fn, Init, Args...>; | |
}; | |
template <class Fn> | |
struct mcurry { | |
template <class... Ts> | |
using call = minvoke<Fn, Ts...>; | |
}; | |
template <class Tp> | |
struct muncurry_; | |
template <template <class...> class Ap, class... As> | |
struct muncurry_<Ap<As...>> { | |
template <class Fn> | |
using call = minvoke<Fn, As...>; | |
}; | |
template <class What, class... With> | |
struct muncurry_<ERROR_<What, With...>> { | |
template <class Fn> | |
using call = ERROR_<What, With...>; | |
}; | |
template <class Fn> | |
struct muncurry { | |
template <class Tp> | |
using call = typename muncurry_<Tp>::template call<Fn>; | |
}; | |
template <class Fn, class List> | |
using mapply = minvoke<muncurry<Fn>, List>; | |
template <bool> | |
struct mconcat_ { | |
template < | |
class... Ts, | |
template <class...> class Ap = types, | |
class... As, | |
template <class...> class Bp = types, | |
class... Bs, | |
template <class...> class Cp = types, | |
class... Cs, | |
template <class...> class Dp = types, | |
class... Ds, | |
class... Tail> | |
static auto call( | |
types<Ts...> *, | |
Ap<As...> *, | |
Bp<Bs...> * = nullptr, | |
Cp<Cs...> * = nullptr, | |
Dp<Ds...> * = nullptr, | |
Tail *...tail) | |
-> decltype(mconcat_<(sizeof...(Tail) == 0)>::call( | |
static_cast<types<Ts..., As..., Bs..., Cs..., Ds...> *>(nullptr), | |
tail...)); | |
}; | |
template <> | |
struct mconcat_<true> { | |
template <class... As> | |
static auto call(types<As...> *) -> types<As...>; | |
}; | |
template <class Continuation = qq<types>> | |
struct mconcat { | |
template <class... Args> | |
using call = mapply< | |
Continuation, | |
decltype(mconcat_<(sizeof...(Args) == 0)>::call({}, static_cast<Args *>(nullptr)...))>; | |
}; | |
struct msize { | |
template <class... Ts> | |
using call = msize_t<sizeof...(Ts)>; | |
}; | |
template <class Ty> | |
struct mcount { | |
template <class... Ts> | |
using call = msize_t<(std::is_same_v<Ts, Ty> + ... + 0)>; | |
}; | |
template <class Fn> | |
struct mcount_if { | |
template <class... Ts> | |
using call = msize_t<(bool(v<minvoke<Fn, Ts>>) + ... + 0)>; | |
}; | |
template <class Tp> | |
struct mcontains { | |
template <class... Args> | |
using call = mbool<(std::is_same_v<Tp, Args> || ...)>; | |
}; | |
template <class Continuation = q<types>> | |
struct mpush_back { | |
template <class List, class Item> | |
using call = mapply<mbind_back<Continuation, Item>, List>; | |
}; | |
template <class...> | |
struct mcompose { }; | |
template <class First> | |
struct mcompose<First> : First { }; | |
template <class Second, class First> | |
struct mcompose<Second, First> { | |
template <class... Args> | |
using call = minvoke<Second, minvoke<First, Args...>>; | |
}; | |
template <class Last, class Penultimate, class... Rest> | |
struct mcompose<Last, Penultimate, Rest...> { | |
template <class... Args> | |
using call = minvoke<Last, minvoke<mcompose<Penultimate, Rest...>, Args...>>; | |
}; | |
template <template <class...> class Second, template <class...> class First> | |
struct mcompose_q { | |
template <class... Args> | |
using call = Second<First<Args...>>; | |
}; | |
template <class Old, class New, class Continuation = q<types>> | |
struct mreplace { | |
template <class... Args> | |
using call = minvoke<Continuation, mif_c<std::is_same_v<Args, Old>, New, Args>...>; | |
}; | |
template <class Old, class Continuation = q<types>> | |
struct mremove { | |
template <class... Args> | |
using call = // | |
minvoke< | |
mconcat<Continuation>, | |
mif_c<std::is_same_v<Args, Old>, types<>, types<Args>>...>; | |
}; | |
template <class Pred, class Continuation = q<types>> | |
struct mremove_if { | |
template <class... Args> | |
using call = // | |
minvoke<mconcat<Continuation>, mif<minvoke<Pred, Args>, types<>, types<Args>>...>; | |
}; | |
template <class Return> | |
struct qf { | |
template <class... Args> | |
using call = Return(Args...); | |
}; | |
template <class Ty, class...> | |
using mfront_ = Ty; | |
template <class... As> | |
using mfront = meval<mfront_, As...>; | |
template <class... As> | |
requires(sizeof...(As) == 1) | |
using msingle = mfront<As...>; | |
template <class Default, class... As> | |
requires(sizeof...(As) <= 1) | |
using msingle_or_ = mfront<As..., Default>; | |
template <class Default> | |
using msingle_or = mbind_front_q<msingle_or_, Default>; | |
template <class Ty> | |
concept has_id = requires { typename Ty::id; }; | |
template <class Ty> | |
struct Id { | |
using t = Ty; | |
// Uncomment the line below to find any code that likely misuses the | |
// ADL isolation mechanism. In particular, 'id<T>' when T is a | |
// reference is a likely misuse. The static_assert below will trigger | |
// when the type passed to the id alias template is a reference to | |
// a type that is setup to use ADL isolation. | |
//static_assert(!has_id<std::remove_cvref_t<Ty>>); | |
}; | |
template <bool = true> | |
struct id_ { | |
template <class Ty> | |
using call = typename Ty::id; | |
}; | |
template <> | |
struct id_<false> { | |
template <class Ty> | |
using call = Id<Ty>; | |
}; | |
template <class Ty> | |
using id = minvoke<id_<has_id<Ty>>, Ty>; | |
template <class From, class To = std::decay_t<From>> | |
using cvref_t = copy_cvref_t<From, t<To>>; | |
template <class From, class To = std::decay_t<From>> | |
using cvref_id = copy_cvref_t<From, id<To>>; | |
#if defined(__NVCOMPILER) | |
// nvc++ doesn't cache the results of alias template specializations. | |
// To avoid repeated computation of the same function return type, | |
// cache the result ourselves in a class template specialization. | |
template <class Fun, class... As> | |
using call_result_ = decltype(std::declval<Fun>()(std::declval<As>()...)); | |
template <class Fun, class... As> | |
using call_result_t = t<mdefer<q<call_result_>, Fun, As...>>; | |
#else | |
template <class Fun, class... As> | |
using call_result_t = decltype(std::declval<Fun>()(std::declval<As>()...)); | |
#endif | |
// BUGBUG TODO file this bug with nvc++ | |
#if defined(__NVCOMPILER) | |
template <const auto &Fun, class... As> | |
using result_of = call_result_t<decltype(Fun), As...>; | |
#else | |
template <const auto &Fun, class... As> | |
using result_of = decltype(Fun(std::declval<As>()...)); | |
#endif | |
template <const auto &Fun, class... As> | |
inline constexpr bool noexcept_of = noexcept(Fun(std::declval<As>()...)); | |
// For emplacing non-movable types into optionals: | |
template <class Fn> | |
requires std::is_nothrow_move_constructible_v<Fn> | |
struct emplace_from { | |
Fn fn_; | |
using t = call_result_t<Fn>; | |
operator t() && noexcept(noexcept(std::declval<Fn>())) { | |
return static_cast<Fn &&>(fn_)(); | |
} | |
auto operator()() && noexcept(noexcept(std::declval<Fn>())) -> t { | |
return static_cast<Fn &&>(fn_)(); | |
} | |
}; | |
template <class Fn> | |
emplace_from(Fn) -> emplace_from<Fn>; | |
template <class, class, class, class> | |
struct mzip_with2_; | |
template < // | |
class Fn, // | |
class Continuation, // | |
template <class...> | |
class Cp, | |
class... Cs, // | |
template <class...> | |
class Dp, | |
class... Ds> | |
requires requires { typename minvoke<Continuation, minvoke<Fn, Cs, Ds>...>; } | |
struct mzip_with2_<Fn, Continuation, Cp<Cs...>, Dp<Ds...>> { | |
using t = minvoke<Continuation, minvoke<Fn, Cs, Ds>...>; | |
}; | |
template <class Fn, class Continuation = q<types>> | |
struct mzip_with2 { | |
template <class Cp, class Dp> | |
using call = t<mzip_with2_<Fn, Continuation, Cp, Dp>>; | |
}; | |
template <bool> | |
struct mfind_if_ { | |
template <class Fn, class Continuation, class Head, class... Tail> | |
using call = // | |
minvoke< | |
mif_c< | |
v<minvoke<Fn, Head>>, | |
mbind_front<Continuation, Head>, | |
mbind_front<mfind_if_<(sizeof...(Tail) != 0)>, Fn, Continuation>>, | |
Tail...>; | |
}; | |
template <> | |
struct mfind_if_<false> { | |
template <class Fn, class Continuation> | |
using call = minvoke<Continuation>; | |
}; | |
template <class Fn, class Continuation = q<types>> | |
struct mfind_if { | |
template <class... Args> | |
using call = minvoke<mfind_if_<(sizeof...(Args) != 0)>, Fn, Continuation, Args...>; | |
}; | |
template <class Fn> | |
struct mfind_if_i { | |
template <class... Args> | |
using call = msize_t<(sizeof...(Args) - v<minvoke<mfind_if<Fn, msize>, Args...>>)>; | |
}; | |
#if defined(_MSC_VER) | |
# define mvalue_of(...) __VA_ARGS__::value | |
#else | |
# define mvalue_of(...) v<__VA_ARGS__> | |
#endif | |
template <class... Booleans> | |
using mand_t = mbool<(mvalue_of(Booleans) && ...)>; | |
template <class... Booleans> | |
using mand = meval<mand_t, Booleans...>; | |
template <class... Booleans> | |
using mor_t = mbool<(mvalue_of(Booleans) || ...)>; | |
template <class... Booleans> | |
using mor = meval<mor_t, Booleans...>; | |
template <class Boolean> | |
using mnot_t = mbool<!mvalue_of(Boolean)>; | |
template <class Boolean> | |
using mnot = meval<mnot_t, Boolean>; | |
#if defined(__NVCOMPILER) | |
template <class... Ints> | |
struct mplus_t : mconstant<(v<Ints> + ...)> { }; | |
#else | |
template <class... Ints> | |
using mplus_t = mconstant<(mvalue_of(Ints) + ...)>; | |
#endif | |
#undef mvalue_of | |
template <class Fn> | |
struct mall_of { | |
template <class... Args> | |
using call = mand<minvoke<Fn, Args>...>; | |
}; | |
template <class Fn> | |
struct mnone_of { | |
template <class... Args> | |
using call = mand<mnot<minvoke<Fn, Args>>...>; | |
}; | |
template <class Fn> | |
struct many_of { | |
template <class... Args> | |
using call = mor<minvoke<Fn, Args>...>; | |
}; | |
#if defined(__cpp_pack_indexing) | |
template <class Np, class... Ts> | |
using m_at = Ts...[v<Np>]; | |
template <std::size_t Np, class... Ts> | |
using m_at_c = Ts...[Np]; | |
#elif META_HAS_BUILTIN(__type_pack_element) | |
template <bool> | |
struct m_at_ { | |
template <class Np, class... Ts> | |
using call = __type_pack_element<v<Np>, Ts...>; | |
}; | |
template <class Np, class... Ts> | |
using m_at = minvoke<m_at_<v<Np> == ~0ul>, Np, Ts...>; | |
template <std::size_t Np, class... Ts> | |
using m_at_c = minvoke<m_at_<Np == ~0ul>, msize_t<Np>, Ts...>; | |
#else | |
template <std::size_t> | |
using void_ptr = void *; | |
template <class Ty> | |
using mtype_ptr = type_identity<Ty> *; | |
template <class Ty> | |
struct m_at_; | |
template <std::size_t... Is> | |
struct m_at_<indices<Is...>> { | |
template <class Up, class... Us> | |
static Up f_(void_ptr<Is>..., Up *, Us *...); | |
template <class... Ts> | |
using call = t<decltype(m_at_::f_(mtype_ptr<Ts>()...))>; | |
}; | |
template <std::size_t Np, class... Ts> | |
using m_at_c = minvoke<m_at_<make_indices<Np>>, Ts...>; | |
template <class Np, class... Ts> | |
using m_at = m_at_c<v<Np>, Ts...>; | |
#endif | |
template <class... Ts> | |
using mback = m_at_c<sizeof...(Ts) - 1, Ts...>; | |
template <class Continuation = q<types>> | |
struct mpop_back { | |
template <class> | |
struct impl; | |
template <std::size_t... Idx> | |
struct impl<indices<Idx...>> { | |
template <class... Ts> | |
using call = minvoke<Continuation, m_at_c<Idx, Ts...>...>; | |
}; | |
template <class... Ts> | |
requires(sizeof...(Ts) != 0) | |
using call = minvoke<impl<make_indices<sizeof...(Ts) - 1>>, Ts...>; | |
}; | |
template <std::size_t Np> | |
struct placeholder { | |
placeholder() = default; | |
constexpr placeholder(void *) noexcept { | |
} | |
constexpr friend auto get_placeholder_offset(placeholder) noexcept -> std::size_t { | |
return Np; | |
} | |
}; | |
#if defined(__cpp_pack_indexing) | |
template <std::size_t Np> | |
struct nth_pack_element_t { | |
template <class... Ts> | |
[[__always_inline__]] | |
constexpr decltype(auto) | |
operator()(Ts &&...ts) const noexcept { | |
static_assert(Np < sizeof...(Ts)); | |
return static_cast<Ts...[Np] &&>(ts...[Np]); | |
} | |
}; | |
#else | |
template <class... Ignore> | |
struct nth_pack_element_impl { | |
template <class Ty, class... Us> | |
[[__always_inline__]] | |
constexpr Ty && | |
operator()(Ignore..., Ty &&t, Us &&...) const noexcept { | |
return static_cast<decltype(t) &&>(t); | |
} | |
}; | |
template <std::size_t Np> | |
struct nth_pack_element_t { | |
template <std::size_t... Is> | |
[[__always_inline__]] | |
static constexpr auto | |
impl(indices<Is...>) noexcept { | |
return nth_pack_element_impl<ignore_t<Is>...>(); | |
} | |
template <class... Ts> | |
[[__always_inline__]] | |
constexpr decltype(auto) | |
operator()(Ts &&...ts) const noexcept { | |
static_assert(Np < sizeof...(Ts)); | |
return impl(make_indices<Np>())(static_cast<Ts &&>(ts)...); | |
} | |
}; | |
#endif | |
template <std::size_t Np> | |
inline constexpr nth_pack_element_t<Np> nth_pack_element{}; | |
template <auto... Vs> | |
struct mliterals { | |
template <std::size_t Np> | |
[[__always_inline__]] | |
static constexpr auto | |
nth() noexcept { | |
return meta::nth_pack_element<Np>(Vs...); | |
} | |
}; | |
template <std::size_t Np> | |
struct nth_member { | |
template <class Ty> | |
[[__always_inline__]] | |
constexpr decltype(auto) | |
operator()(Ty &&ty) const noexcept { | |
return static_cast<Ty &&>(ty).*(ty.mbrs_.template nth<Np>()); | |
} | |
}; | |
template <class Set, class... Ty> | |
concept mset_contains = (std::is_base_of_v<type_identity<Ty>, Set> && ...); | |
namespace set { | |
template <class... Ts> | |
struct inherit { }; | |
template <class Ty, class... Ts> | |
struct [[__ebo__]] inherit<Ty, Ts...> | |
: type_identity<Ty> | |
, inherit<Ts...> { }; | |
template <class... Set> | |
auto operator+(inherit<Set...> &) -> inherit<Set...>; | |
template <class... Set, class Ty> | |
auto operator%(inherit<Set...> &, type_identity<Ty> &) // | |
-> mif_c< // | |
mset_contains<inherit<Set...>, Ty>, | |
inherit<Set...>, | |
inherit<Ty, Set...>> &; | |
template <class ExpectedSet, class... Ts> | |
concept mset_eq = // | |
(sizeof...(Ts) == v<mapply<msize, ExpectedSet>>) && // | |
mset_contains<ExpectedSet, Ts...>; | |
template <class ExpectedSet> | |
struct eq { | |
template <class... Ts> | |
using call = mbool<mset_eq<ExpectedSet, Ts...>>; | |
}; | |
} // namespace set | |
template <class... Ts> | |
using mset = set::inherit<Ts...>; | |
template <class Set, class... Ts> | |
using mset_insert = decltype(+(std::declval<Set &>() % ... % std::declval<type_identity<Ts> &>())); | |
template <class... Ts> | |
using mmake_set = mset_insert<mset<>, Ts...>; | |
template <class Set1, class Set2> | |
concept mset_eq = v<mapply<set::eq<Set1>, Set2>>; | |
template <class Continuation = q<types>> | |
struct munique { | |
template <class... Ts> | |
using call = mapply<Continuation, mmake_set<Ts...>>; | |
}; | |
} // namespace meta |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment