It is possible to deduce arity of a non-generic lambda by accessing its operator()
.
template
struct fInfo : fInfo
This is a c++17 solution that works with generic and variadic lambdas and functors with variadic templatet operator(). the idea is to recursively simulate the call with descending number of arguments and use SFINAE to break recursion when the first matching number of arguments is found. It compilers on gcc >= 7 and Clang >=5. A working example can be found here.
#include
constexpr size_t max_arity = 10;
struct variadic_t
{
};
namespace detail
{
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hece, to 'simulate' an arbitrary function signature.
template
struct arbitrary_t
{
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template
operator T &&();
template
operator T &();
};
template ()(arbitrary_t{}...))>
constexpr auto test_signature(std::index_sequence)
{
return std::integral_constant{};
}
template
constexpr auto arity_impl(int) -> decltype(test_signature(std::make_index_sequence{}))
{
return {};
}
template 0)>>
constexpr auto arity_impl(...)
{
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl(0);
}
template
constexpr auto arity_impl()
{
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl(0);
if constexpr (tmp == MaxArity + 1)
{
// if that works, F is considered variadic
return variadic_t{};
}
else
{
// if not, tmp will be the correct arity of F
return tmp;
}
}
}
template
constexpr auto arity(F&& f) { return detail::arity_impl, MaxArity>(); }
template
constexpr auto arity_v = detail::arity_impl, MaxArity>();
template
constexpr bool is_variadic_v = std::is_same_v)>, variadic_t>;
Usage:
auto l = [](auto...){};
static_assert(is_variadic_v);
and:
auto l = [](auto, auto, auto){};
static_assert(!is_variadic_v);
static_assert(arity(l) == 3);