I want to create a function that counts the number of arguments in generic lambda functions. SO user "yuri kilochek" has a great solution Count the number of arguments in a lambda but it does not work on functions that take reference types:
#include <iostream>
#include <utility>
#include <type_traits>
struct any_argument {
template <typename T>
operator T&&() const;
};
template <typename Lambda, typename Is, typename = void>
struct can_accept_impl
: std::false_type
{};
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl<Lambda, std::index_sequence<Is...>,
decltype(std::declval<Lambda>()(((void)Is, any_argument{})...), void())>
: std::true_type
{};
template <typename Lambda, std::size_t N>
struct can_accept
: can_accept_impl<Lambda, std::make_index_sequence<N>>
{};
template <typename Lambda, std::size_t Max, std::size_t N, typename = void>
struct lambda_details_impl
: lambda_details_impl<Lambda, Max, N - 1>
{};
template <typename Lambda, std::size_t Max, std::size_t N>
struct lambda_details_impl<Lambda, Max, N, std::enable_if_t<can_accept<Lambda, N>::value>>
{
static constexpr bool is_variadic = (N == Max);
static constexpr std::size_t argument_count = N;
};
template <typename Lambda, std::size_t Max = 50>
struct lambda_details
: lambda_details_impl<Lambda, Max, Max>
{};
int main()
{
auto lambda0 = []() {};
auto lambda1 = [](int a) {};
auto lambda2 = [](int a, auto b) {};
auto lambda3 = [](int a, auto b, char = 'a') {};
auto lambda4 = [](int a, auto b, char = 'a', auto...) {};
auto lambda5 = [](int a, auto& b){};
std::cout << lambda_details<decltype(lambda0)>::is_variadic << " " << lambda_details<decltype(lambda0)>::argument_count << "\n"; // 0 0
std::cout << lambda_details<decltype(lambda1)>::is_variadic << " " << lambda_details<decltype(lambda1)>::argument_count << "\n"; // 0 1
std::cout << lambda_details<decltype(lambda2)>::is_variadic << " " << lambda_details<decltype(lambda2)>::argument_count << "\n"; // 0 2
std::cout << lambda_details<decltype(lambda3)>::is_variadic << " " << lambda_details<decltype(lambda3)>::argument_count << "\n"; // 0 3
std::cout << lambda_details<decltype(lambda4)>::is_variadic << " " << lambda_details<decltype(lambda4)>::argument_count << "\n"; // 1 50
std::cout << lambda_details<decltype(lambda5)>::is_variadic << " " << lambda_details<decltype(lambda5)>::argument_count << "\n"; // 1 50
}
Here is the result https://godbolt.org/z/35envxjfz
I feel just a minor change to the code can make it work, but I am relatively new to template metaprogramming. How to achieve what I want?
Thanks!
Since you are passing any_argument{}
, i.e. an rvalue cannot be bound to an lvalue reference, the compilation fails.
The simple workaround is to pass in an lvalue reference of any_argument
, e.g.
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl<Lambda, std::index_sequence<Is...>,
decltype(std::declval<Lambda>()((
(void)Is, std::declval<any_argument&>())...), void())>
: std::true_type
{};