It is possible to deduce arity of a non-generic lambda by accessing its operator()
.
template
struct fInfo : fInfo
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.
#include
struct fake_anything {
fake_anything(fake_anything const&);
fake_anything();
fake_anything&operator=(fake_anything const&);
templateoperator T&() const;
templateoperator T&&() const;
templateoperator T const&() const;
templateoperator T const&&() const;
fake_anything operator*() const;
fake_anything operator++() const;
fake_anything operator++(int) const;
fake_anything operator->() const;
templatefake_anything(T&&);
};
fake_anything operator+(fake_anything, fake_anything);
fake_anything operator-(fake_anything, fake_anything);
fake_anything operator*(fake_anything, fake_anything);
fake_anything operator/(fake_anything, fake_anything);
// etc for every operator
templateusing void_t=void;
template
struct can_invoke:std::false_type{};
template
struct can_invoke()( std::declval()... ) ) >
> : std::true_type
{};
templatestruct is_sig:std::false_type{};
templatestruct is_sig:std::true_type{};
templatestruct indexes{using type=indexes;};
templatestruct make_indexes:make_indexes{};
templatestruct make_indexes<0,Is...>:indexes{};
templateusing make_indexes_t=typename make_indexes::type;
templateusing unpacker=T;
template
struct nary_help;
template
struct nary_help>:
can_invoke... )>
{};
template
struct has_n_arity:
nary_help>
{};
template
struct max_arity{
enum{Mid=(Max+Min)/2};
enum{
lhs = max_arity::value,
rhs = max_arity::value,
value = lhs>rhs?lhs:rhs,
};
};
template
struct max_arity:
std::integral_constant::value?(int)X:-1>
{};
template
struct min_arity{
enum{Mid=(Max+Min)/2};
enum{
lhs = min_arity::value,
rhs = min_arity::value,
value = lhs
struct min_arity:
std::integral_constant::value?X:(unsigned)-1>
{};
auto test1 = [](auto x, auto y)->bool { return x < y; };
auto test2 = [](auto x, auto y) { return x + y; };
auto test3 = [](auto x) { return x.y; };
int main() {
std::cout << can_invoke< decltype(test1)( fake_anything, fake_anything ) >::value << "\n";
std::cout << can_invoke< decltype(test1)( int, int ) >::value << "\n";
std::cout << has_n_arity< decltype(test1), 2 >::value << "\n";
std::cout << max_arity< decltype(test1) >::value << "\n";
std::cout << max_arity< decltype(test2) >::value << "\n";
// will fail to compile:
// std::cout << max_arity< decltype(test3) >::value << "\n";
}
live example.
Note sufficient SFINAE will mean the above will get the wrong result, as will use of operator.
, or use of operator.
on certain kinds of "derived" types, or accessing types based off of the fake_anything
parameter, etc.
However, if the lambda specifies its return value with a ->X
clause, then fake_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).