I need a method of figuring out a function's argument types, and so I wrote a closure_traits class, given below, as inspired by Is it possible to figure out the parameter type and return type of a lambda?.
However, when I try to apply it to a simple lambda, I get the error that 'operator()' is not a member of '(lambda type)'. However, according to cppreference, lambda's do have an operator(). I also tried using std::function, and got the equivalent error. I guess I'm not sure what's going wrong, and any help would be greatly appreciated.
#include<type_traits>
#include<tuple>
#include<utility>
#include<iostream>
/* For generic types use the type signature of their operator() */
template <typename T>
struct closure_traits : public
closure_traits<decltype(&T::operator())> {};
/* Otherwise, we do a template match on a function type. */
template <typename ClassType, typename ReturnType,
typename... ArgTypes>
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)>
{
using arity = std::integral_constant<std::size_t,
sizeof...(ArgTypes)>;
using Ret = ReturnType;
/* The argument types will be the same as the types of the
* elements of a tuple composed of them.
*/
template <std::size_t I>
struct Args {
using type = typename std::tuple_element<I,
std::tuple<ArgTypes...>>::type;
};
};
int main() {
auto thing = [=] (int x) {return x;};
std::cerr << "The number of arguments is "
<< closure_traits<decltype(thing)>::arity << std::endl;
return 0;
}
The compiler error messages that I get are below. My compile command is simply g++ -std=c++14 main.cpp.
main.cpp: In instantiation of ‘struct closure_traits<int (main()::<lambda(int)>::*)(int) const>’:
main.cpp:9:8: required from ‘struct closure_traits<main()::<lambda(int)> >’
main.cpp:34:82: required from here
main.cpp:9:56: error: ‘operator()’ is not a member of ‘int (main()::<lambda(int)>::*)(int) const’
struct closure_traits : public closure_traits<decltype(&T::operator())> {};
^
main.cpp: In function ‘int main()’:
main.cpp:34:51: error: ‘arity’ is not a member of ‘closure_traits<main()::<lambda(int)> >’
std::cerr << "The number of arguments is " << closure_traits<decltype(thing)>::arity << std::endl;
In case anyone has trouble with this the correct code is as follows :
Your specialization does not match the
decltype(&T::operator())
argument.Because of this, instead of choosing the specialization (as you wanted it to), the compiler is forced to recursively choose the same main template. Which makes it to apply
&T::operator()
expression again, after it was already applied once. I.e the initial attempt to do&T::operator()
actually succeeds, but then the compiler attempts to apply&T::operator()
again, whenT
is alreadyint (main()::<lambda(int)>::*)(int) const
. The latter, obviously, does not haveoperator ()
, which is why you are getting this error message.The reason it cannot choose your specialization is missing
const
in template parameter declaration. The lambda'soperator ()
is actually aconst
member of the lambda class. Addconst
to the declaration of your specializationan the compiler will follow the specialization path you intended it to follow.
And, of course, you have to print
closure_traits<decltype(thing)>::arity::value
, not justclosure_traits<decltype(thing)>::arity
.The compiler correctly gets the
operator()
of the lambda type, but a pointer to that member function does not match your specialization because of theconst
qualifier.You should add a second specialization
(Yes, writing templates to take a function type is painful.)