I don't know a way to count all argument of a generic-lambda [edit: but yuri kilochek know how to do it: see his answer for a great solution].
For non-generic lambdas, as suggested by Igor Tandetnik, you can detect the types (return and arguments) of the pointer to operator()
and count the arguments.
Something as follows
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...) const)
{ return sizeof...(Args); }
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...))
{ return sizeof...(Args); }
template <typename L>
constexpr auto countArguments (L)
{ return cah(&L::operator()); }
But, unfortunately, this doesn't works when you introduce an auto
argument because, with an auto
argument, you transform operator()
in a template function.
About detecting a variadic lambda, you can detect a function with only a variadic list of arguments (let me call it "pure variadic"), as your lambda_variadic
, trying to call it with zero and with (by example) 50 argument of a given type.
I mean something as follows
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename T, std::size_t N>
using getType_t = typename getType<T, N>::type;
// isPureVariadic arguments helper
template <typename T>
constexpr std::false_type ipvh (...);
// isPureVariadic arguments helper
template <typename T, typename F, std::size_t ... Is>
constexpr auto ipvh (F f, std::index_sequence<Is...>)
-> decltype( f(std::declval<getType_t<T, Is>>()...), std::true_type{} );
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value; }
but this isn't perfect because gives false positives and false negatives.
A problem is that when you check it with a "not pure variadic lambda" as
auto lambda_variadic2 = [&](std::string, auto... args){ ... };
that is variadic but the first argument doesn't accept a int
, isn't detected as "pure variadic"; unfortunately the following lambda
auto lambda_variadic3 = [&](long, auto... args){ ... };
is detected as "pure variadic" because the first argument accept a int
.
To avoid this problem, you can modify the function to check the call with 50 arguments of two incompatible types; by example
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value
&& decltype(ipvh<std::string>(f, std::make_index_sequence<50u>{}))::value; }
Another problem is that are detected as "pure virtual" also non-variadic generic-lambda functions receiving a number of arguments higher that the checked number (50, in the example).
And remain the problem that this solution doesn't detect lambda_variadic2
(a non-pure variadic lambda) as variadic.
The following is a full compiling example with the best I can imagine about your question
#include <iostream>
#include <utility>
#include <type_traits>
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...) const)
{ return sizeof...(Args); }
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...))
{ return sizeof...(Args); }
template <typename L>
constexpr auto countArguments (L)
{ return cah(&L::operator()); }
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename T, std::size_t N>
using getType_t = typename getType<T, N>::type;
// isPureVariadic arguments helper
template <typename T>
constexpr std::false_type ipvh (...);
// isPureVariadic arguments helper
template <typename T, typename F, std::size_t ... Is>
constexpr auto ipvh (F f, std::index_sequence<Is...>)
-> decltype( f(std::declval<getType_t<T, Is>>()...), std::true_type{} );
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value; }
int main() {
auto lambda0 = [&]() {};
auto lambda1 = [&](int) {};
auto lambda2 = [&](int, auto) {};
auto lambda3 = [&](auto...) {};
std::cout << countArguments(lambda0) << std::endl;
std::cout << countArguments(lambda1) << std::endl;
// std::cout << countArguments(lambda2) << std::endl; // compilation error
// std::cout << countArguments(lambda3) << std::endl; // compilation error
std::cout << isPureVariadic(lambda0) << std::endl;
std::cout << isPureVariadic(lambda1) << std::endl;
std::cout << isPureVariadic(lambda2) << std::endl;
std::cout << isPureVariadic(lambda3) << std::endl;
}
operator()
). Off the top, I'm not sure how to extend it to other cases. – Gerge