It is possible to deduce arity of a non-generic lambda by accessing its operator()
.
template <typename F>
struct fInfo : fInfo<decltype(&F::operator())> { };
template <typename F, typename Ret, typename... Args>
struct fInfo<Ret(F::*)(Args...)const> { static const int arity = sizeof...(Args); };
This is nice and dandy for something like [](int x){ return x; }
as the operator()
is not templated.
However, generic lambdas do template the operator()
and it is only possible to access a concrete instantiation of the template - which is slightly problematic because I can't manually provide template arguments for the operator()
as I don't know what its arity is.
So, of course, something like
auto lambda = [](auto x){ return x; };
auto arity = fInfo<decltype(lambda)>::arity;
doesn't work.
I don't know what to cast to nor do I know what template arguments to provide (or how many) (operator()<??>
).
Any ideas how to do this?
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.
Usage:
and:
This technique will work in some cases. I create a
fake_anything
type that can fake almost anything, and try to invoke your lambda with some number of instances of that.live example.
Note sufficient SFINAE will mean the above will get the wrong result, as will use of
operator.
, or use ofoperator.
on certain kinds of "derived" types, or accessing types based off of thefake_anything
parameter, etc.However, if the lambda specifies its return value with a
->X
clause, thenfake_anything
is more than good enough. The hard part is dealing with the body.Note that this approach is often a bad idea, because if you want to know the arity of a function, you probably also know the types of the things you want to invoke the function object with! And above I answer that question really easily (can this function object be invoked with these arguments?). It can even be improved to ask "what is the longest/shortest prefix of these arguments that can invoke this function object", or handle "how many repeats of type X work to invoke this function object" (if you want clean failure, you need an upper bound).
It's impossible, as the function call operator can be a variadic template. It's been impossible to do this forever for function objects in general, and special-casing lambdas because they happened to not be equally powerful was always going to be a bad idea. Now it's just time for that bad idea to come home to roost.
I would say this is partially possible, at least you can know the overall arity (templated + regular types) when you explicitly instantiate the auto parameters of
operator()
:Another posible solution, for case when possible template types are known: http://coliru.stacked-crooked.com/a/e3a07d723a8f27e9