Created
May 2, 2022 20:19
-
-
Save schaumb/b28559cf8f5490561825e63baa3b180f to your computer and use it in GitHub Desktop.
lambda traits header only lib. min/max arity, has capture, is variadic etc
This file contains hidden or 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
#include <tuple> | |
#include <limits> | |
#define BXLX_MAXIMUM_ARG_TEMPLATE_LAMBDA 100 | |
namespace bxlx { | |
namespace impl { | |
template<std::size_t> | |
struct arg { | |
using type = arg; | |
using value_type = arg; | |
template<class Res, class ...Args> | |
using fun_ptr = Res(*) (Args...) noexcept; | |
fun_ptr<std::size_t> size; | |
fun_ptr<arg*> data; | |
fun_ptr<arg*> begin; | |
fun_ptr<arg*> end; | |
constexpr arg() noexcept = delete; | |
template<typename T> | |
constexpr operator T() const noexcept { | |
return *this; | |
} | |
template<std::size_t O> | |
constexpr operator arg<O>() const noexcept { | |
return *this; | |
} | |
friend constexpr arg operator<(arg a, arg) { return a; } | |
// TODO add more operator ... | |
}; | |
template<class T> | |
struct Fix { | |
template<std::size_t> | |
using type = T; | |
}; | |
template<> | |
struct Fix<void> { | |
template<std::size_t N> | |
using type = arg<N>; | |
}; | |
enum function_signs { | |
variadic, | |
const_, | |
volatile_, | |
lref, | |
rref, | |
noexcept_ | |
}; | |
template<class Ret, class Args, function_signs... signs> | |
struct function_traits_base; | |
template<class Ret, class... Args, function_signs... signs> | |
struct function_traits_base<Ret, std::tuple<Args...>, signs...> { | |
constexpr static auto arity = sizeof...(Args); | |
constexpr static bool is_variadic_v = ((signs == variadic) || ...); | |
constexpr static bool is_const_v = ((signs == const_) || ...); | |
constexpr static bool is_volatile_v = ((signs == volatile_) || ...); | |
constexpr static bool is_lvalue_reference_v = ((signs == lref) || ...); | |
constexpr static bool is_rvalue_reference_v = ((signs == rref) || ...); | |
constexpr static bool is_noexcept_v = ((signs == noexcept_) || ...); | |
using return_type = Ret; | |
using args = std::tuple<Args...>; | |
using fun_ptr = | |
std::conditional_t<is_noexcept_v, | |
std::conditional_t<is_variadic_v, | |
Ret(*)(Args..., ...) noexcept, | |
Ret(*)(Args...) noexcept | |
>, | |
std::conditional_t<is_variadic_v, | |
Ret(*)(Args..., ...), | |
Ret(*)(Args...) | |
> | |
>; | |
}; | |
template<class> | |
struct function_traits; | |
// specialization for regular functions | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...)> : function_traits_base<Ret, std::tuple<Args...>> {}; | |
// specialization for variadic functions such as std::printf | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...)> : function_traits_base<Ret, std::tuple<Args...>, variadic> {}; | |
// specialization for function types that have cv-qualifiers | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const> : function_traits_base<Ret, std::tuple<Args...>, const_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) volatile> : function_traits_base<Ret, std::tuple<Args...>, volatile_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const volatile> : function_traits_base<Ret, std::tuple<Args...>, const_, volatile_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) volatile> : function_traits_base<Ret, std::tuple<Args...>, variadic, volatile_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const volatile> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, volatile_> {}; | |
// specialization for function types that have ref-qualifiers | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) &> : function_traits_base<Ret, std::tuple<Args...>, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const &> : function_traits_base<Ret, std::tuple<Args...>, const_, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) volatile &> : function_traits_base<Ret, std::tuple<Args...>, volatile_, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const volatile &> : function_traits_base<Ret, std::tuple<Args...>, const_, volatile_, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) &> : function_traits_base<Ret, std::tuple<Args...>, variadic, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const &> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) volatile &> : function_traits_base<Ret, std::tuple<Args...>, variadic, volatile_, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const volatile &> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, volatile_, lref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) &&> : function_traits_base<Ret, std::tuple<Args...>, rref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const &&> : function_traits_base<Ret, std::tuple<Args...>, const_, rref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) volatile &&> : function_traits_base<Ret, std::tuple<Args...>, volatile_, rref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const volatile &&> : function_traits_base<Ret, std::tuple<Args...>, const_, volatile_, rref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) &&> : function_traits_base<Ret, std::tuple<Args...>, variadic, rref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const &&> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, rref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) volatile &&> : function_traits_base<Ret, std::tuple<Args...>, variadic, volatile_, rref> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const volatile &&> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, volatile_, rref> {}; | |
// specializations for noexcept versions of all the above (C++17 and later) | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) noexcept> : function_traits_base<Ret, std::tuple<Args...>, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const noexcept> : function_traits_base<Ret, std::tuple<Args...>, const_, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) volatile noexcept> : function_traits_base<Ret, std::tuple<Args...>, volatile_, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const volatile noexcept> : function_traits_base<Ret, std::tuple<Args...>, const_, volatile_, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) volatile noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, volatile_, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const volatile noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, volatile_, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) & noexcept> : function_traits_base<Ret, std::tuple<Args...>, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const & noexcept> : function_traits_base<Ret, std::tuple<Args...>, const_, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) volatile & noexcept> : function_traits_base<Ret, std::tuple<Args...>, volatile_, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const volatile & noexcept> : function_traits_base<Ret, std::tuple<Args...>, const_, volatile_, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) & noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const & noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) volatile & noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, volatile_, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const volatile & noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, volatile_, lref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) && noexcept> : function_traits_base<Ret, std::tuple<Args...>, rref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const && noexcept> : function_traits_base<Ret, std::tuple<Args...>, const_, rref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) volatile && noexcept> : function_traits_base<Ret, std::tuple<Args...>, volatile_, rref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args...) const volatile && noexcept> : function_traits_base<Ret, std::tuple<Args...>, const_, volatile_, rref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) && noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, rref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const && noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, rref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) volatile && noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, volatile_, rref, noexcept_> {}; | |
template<class Ret, class... Args> | |
struct function_traits<Ret(Args..., ...) const volatile && noexcept> : function_traits_base<Ret, std::tuple<Args...>, variadic, const_, volatile_, rref, noexcept_> {}; | |
template<class MemFun, class = void> | |
struct extract_function; | |
template<class Mem, class Fun> | |
struct extract_function<Fun (Mem::*), std::enable_if_t<std::is_function_v<Fun>>> { | |
using type = Fun; | |
}; | |
template<class MemFun> | |
using extract_function_t = typename extract_function<MemFun>::type; | |
template<class L, class Tup, class V = void, std::size_t ... Ix> | |
constexpr auto min_callable_v = min_callable_v<L, Tup, V, Ix..., sizeof...(Ix)>; | |
template<class L, class Tup, std::size_t ... Ix> | |
constexpr auto min_callable_v<L, Tup, std::enable_if_t<(std::tuple_size_v<Tup> <= sizeof...(Ix))>, Ix...> = | |
sizeof...(Ix); | |
template<class L, class Tup, std::size_t ... Ix> | |
constexpr auto min_callable_v<L, Tup, | |
std::enable_if_t<(std::tuple_size_v<Tup> > sizeof...(Ix))> | |
, Ix...> = | |
std::is_invocable_v<L, std::tuple_element_t<Ix, Tup>...> ? sizeof...(Ix) : | |
min_callable_v<L, Tup, void, Ix..., sizeof...(Ix)>; | |
template<class T, class = void> | |
constexpr bool has_invocable_operator = false; | |
template<class T> | |
constexpr bool has_invocable_operator<T, std::void_t<decltype(&T::operator ())>> = true; | |
template<class T, class = void, class ... Ts> | |
constexpr bool has_template_invocable_operator = false; | |
template<class T, class ... Ts> | |
constexpr bool has_template_invocable_operator<T, | |
std::void_t<decltype(&T::template operator () <Ts...> )>, Ts...> = true; | |
template<class L, template<std::size_t> class, class V = void, class ... Interfaces> | |
struct lambda_traits_impl; | |
template<class L, template<std::size_t> class ArgPlaceholder> | |
struct lambda_traits_impl<L, ArgPlaceholder, std::enable_if_t<has_invocable_operator<L>>> | |
: function_traits<extract_function_t<decltype(&L::operator ())>> | |
{ | |
using base = function_traits<extract_function_t<decltype(&L::operator ())>>; | |
constexpr static auto min_arity = min_callable_v<L, typename base::args>; | |
constexpr static auto max_arity = base::is_variadic_v ? | |
std::numeric_limits<decltype(base::arity)>::max() : base::arity; | |
constexpr static bool has_capture_v = !std::is_convertible_v<L, typename base::fun_ptr>; | |
}; | |
template<class L, template<std::size_t> class ArgPlaceholder, class ... Interfaces> | |
struct lambda_traits_impl<L, ArgPlaceholder, | |
std::enable_if_t<has_template_invocable_operator<L, void, Interfaces...> >, Interfaces...> | |
: function_traits<extract_function_t<decltype(&L::template operator () <Interfaces...> )>> | |
{ | |
using base = function_traits<extract_function_t<decltype(&L::template operator () <Interfaces...> )>>; | |
constexpr static auto is_variadic_v = base::is_variadic_v || | |
has_template_invocable_operator<L, void, Interfaces..., | |
ArgPlaceholder<sizeof...(Interfaces)> | |
>; | |
constexpr static auto min_arity = min_callable_v<L, typename base::args>; | |
constexpr static auto max_arity = is_variadic_v ? | |
std::numeric_limits<decltype(base::arity)>::max() : base::arity; | |
constexpr static auto template_arity = sizeof...(Interfaces); | |
constexpr static auto min_template_arity = template_arity; | |
constexpr static auto max_template_arity = | |
has_template_invocable_operator<L, void, Interfaces..., | |
ArgPlaceholder<sizeof...(Interfaces)> | |
> ? | |
max_arity : template_arity; | |
constexpr static bool has_capture_v = | |
!std::is_convertible_v<L, typename base::fun_ptr>; | |
}; | |
template<class L, template<std::size_t> class ArgPlaceholder, class ... Interfaces> | |
struct lambda_traits_impl<L, ArgPlaceholder, | |
std::enable_if_t<!has_invocable_operator<L> && | |
!has_template_invocable_operator<L, void, Interfaces...> && | |
(sizeof...(Interfaces) <= BXLX_MAXIMUM_ARG_TEMPLATE_LAMBDA) >, Interfaces...> | |
: lambda_traits_impl<L, ArgPlaceholder, void, Interfaces..., | |
ArgPlaceholder<sizeof...(Interfaces)>> | |
{ | |
}; | |
template<class L, template<std::size_t> class ArgPlaceholder, class ... Interfaces> | |
struct lambda_traits_impl<L, ArgPlaceholder, | |
std::enable_if_t<!has_invocable_operator<L> && | |
!has_template_invocable_operator<L, void, Interfaces...> && | |
(sizeof...(Interfaces) > BXLX_MAXIMUM_ARG_TEMPLATE_LAMBDA) >, Interfaces... > { | |
static_assert(sizeof...(Interfaces) <= BXLX_MAXIMUM_ARG_TEMPLATE_LAMBDA, "Unrecognized template argumented lambda. Please add lambda_traits second template argument as usable interface to recognize lambda's operator()"); | |
}; | |
} | |
template<class L, template<std::size_t> class ArgPlaceholder> | |
using lambda_traits_args = impl::lambda_traits_impl<L, ArgPlaceholder>; | |
template<class L, class FixArg = void> | |
using lambda_traits = lambda_traits_args<L, impl::Fix<FixArg>::template type>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment