Skip to content

Instantly share code, notes, and snippets.

@ericniebler
Created September 17, 2024 18:24
Show Gist options
  • Save ericniebler/cba16cb75d27631327b7235ef4c1e9f9 to your computer and use it in GitHub Desktop.
Save ericniebler/cba16cb75d27631327b7235ef4c1e9f9 to your computer and use it in GitHub Desktop.
meta-programming library
/*
* 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