Skip to content

Instantly share code, notes, and snippets.

@notfoundry
Last active October 6, 2020 19:32
Show Gist options
  • Select an option

  • Save notfoundry/fe1385de99133048d552e543d81db719 to your computer and use it in GitHub Desktop.

Select an option

Save notfoundry/fe1385de99133048d552e543d81db719 to your computer and use it in GitHub Desktop.
Universal type-level `apply` metafunction
#include <type_traits
#include "meta_invoke.hpp"
#define ASSERT_SAME(...) static_assert(std::is_same_v<__VA_ARGS__>)
struct f1 {
template <class T>
using f = T*;
};
ASSERT_SAME(META_INVOKE((f1), int), int*);
template <class T>
using f2 = T*;
ASSERT_SAME(META_INVOKE((f2), int), int*);
template <auto X>
using f3 = decltype(X);
ASSERT_SAME(META_INVOKE((f3), 1), int);
struct f4 {
template <auto X>
using f = decltype(X);
};
ASSERT_SAME(META_INVOKE((f4), 1), int);
struct f5 {
template <auto X>
constexpr static auto f = X;
};
static_assert(META_INVOKE((f5), 1) == 1);
constexpr auto f6 = []<auto X>() -> decltype(X) {};
ASSERT_SAME(META_INVOKE((f6), 1), int);
#include <experimental/type_traits>
namespace detail {
template <class... Fs> struct overload : Fs... { using Fs::operator()...; };
template <class... Fs> overload(Fs...) -> overload<Fs...>;
template <class T> struct type_ { using dependant = T; };
template <auto X> struct value_ { constexpr static auto dependant = X; };
template <bool>
struct dcallf_class {
template <template <class...> class F1, class... Ts>
using f = F1<Ts...>;
};
template <>
struct dcallf_class<false> {
template <template <class...> class F1, class... Ts>
using f = F1<>;
};
template <bool>
struct dcallf_auto {
template <template <auto...> class F1, auto... Ts>
using f = F1<Ts...>;
};
template <>
struct dcallf_auto<false> {
template <template <auto...> class F1, auto... Ts>
using f = F1<>;
};
template <template <class...> class F>
struct lift_tmetafunction {
template <class... Ts>
using f = type_<typename dcallf_class<(sizeof...(Ts) > 0)>::template f<F, Ts...>>;
};
template <template <auto...> class F>
struct lift_vmetafunction {
template <auto... Ts>
using f = type_<typename dcallf_auto<(sizeof...(Ts) > 0)>::template f<F, Ts...>>;
};
template <auto TypeF, auto ValueF>
struct dispatch_type_or_value {
template <class F>
using try_instantiate_typef = decltype(TypeF.template operator()<F>());
template <class F>
using try_instantiate_valuef = decltype(ValueF.template operator()<F>());
template <class F>
static auto f() {
if constexpr (std::experimental::is_detected<try_instantiate_typef, F>::value) {
return type_<decltype(TypeF.template operator()<F>())>();
} else if (std::experimental::is_detected<try_instantiate_valuef, F>::value) {
return value_<ValueF.template operator()<F>()>();
} else {
throw;
}
}
};
}
#define META_INVOKE_EAT_PARENS(...) __VA_ARGS__
#define META_INVOKE(fn, ...) \
decltype(detail::overload( \
[]<class F>() -> decltype(detail::dispatch_type_or_value< \
[]<class G>() -> typename G::template f<__VA_ARGS__> { throw; }, \
[]<class G>(){ return G::template f<__VA_ARGS__>; } \
>::template f<F>()) { throw; }, \
[]<template <class...> class F>() -> typename detail::lift_tmetafunction<F>::template f<__VA_ARGS__> { throw; }, \
[]<template <auto...> class F>() -> typename detail::lift_vmetafunction<F>::template f<__VA_ARGS__> { throw; }, \
[]<auto F>() -> detail::type_<decltype(F.template operator()<__VA_ARGS__>())> { throw; } \
).template operator()<META_INVOKE_EAT_PARENS fn>())::dependant
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment