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?
Best Answer
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).