Thread overview
How to get number of parameters in lambda?
Mar 08, 2021
Andrey Zherikov
Mar 09, 2021
Paul Backus
Mar 09, 2021
Andrey Zherikov
Mar 09, 2021
Paul Backus
Mar 09, 2021
Paul Backus
Mar 09, 2021
Adam D. Ruppe
Mar 09, 2021
Andrey Zherikov
Mar 09, 2021
Paul Backus
March 08, 2021
What should I use to get number of lambda arguments? Like get 2 for "(arg1, arg2) {}". std.traits.Parameters doesn't work because isCallable returns false.

    alias f = (arg1, arg2) {};

    writeln(isCallable!f);   // prints false


Specifying argument types works, e.g. isCallable returns true for (int arg1, int arg2) {}, but I'm looking for a solution for type-less lambdas.
March 09, 2021
On Monday, 8 March 2021 at 23:07:18 UTC, Andrey Zherikov wrote:
> What should I use to get number of lambda arguments? Like get 2 for "(arg1, arg2) {}". std.traits.Parameters doesn't work because isCallable returns false.
>
>     alias f = (arg1, arg2) {};
>
>     writeln(isCallable!f);   // prints false
>
>
> Specifying argument types works, e.g. isCallable returns true for (int arg1, int arg2) {}, but I'm looking for a solution for type-less lambdas.

Typeless lambdas are templates, and there's no way to examine the arguments of a template function without first instantiating it, so I'm afraid you're out of luck.

What's the larger problem you're trying to solve here?
March 09, 2021
On Tuesday, 9 March 2021 at 01:38:52 UTC, Paul Backus wrote:
> What's the larger problem you're trying to solve here?

Just to make things simple: I have a 'caller' that calls some function provided as an alias template argument and I want to support different number of arguments (purpose is to provide additional parameters if 'f' supports):

void caller(alias f)()
{
    static if(/*one parameter*/)
        f(1);
    else static if(/*two parameters*/)
        f(1, 2);
}

I understand that types are not available since they are undefined until instantiation. But why can't I get argument count? Is it possible that it can change during instantiation?
March 09, 2021
On Tuesday, 9 March 2021 at 02:24:07 UTC, Andrey Zherikov wrote:
> On Tuesday, 9 March 2021 at 01:38:52 UTC, Paul Backus wrote:
>> What's the larger problem you're trying to solve here?
>
> Just to make things simple: I have a 'caller' that calls some function provided as an alias template argument and I want to support different number of arguments (purpose is to provide additional parameters if 'f' supports):
>
> void caller(alias f)()
> {
>     static if(/*one parameter*/)
>         f(1);
>     else static if(/*two parameters*/)
>         f(1, 2);
> }

If you know the arguments you want to call the function with, the easiest way to check if you can do it is with __traits(compiles):

static if (__traits(compiles, f(1)))
    f(1);
else static if (__traits(compiles, f(1, 2)))
    f(2);

> I understand that types are not available since they are undefined until instantiation. But why can't I get argument count? Is it possible that it can change during instantiation?

Yes, it's possible. For example:

template fun(T) {
    static if (is(T == int)) {
        void fun(T a, T b) {
            /* ... */
        }
    } else {
        void fun(T a) {
            /* ... */
        }
    }
}

Of course, code like this is rare in practice, but because it's technically possible, the compiler can't assume *anything* about a template function before it's been instantiated.
March 09, 2021
On Tuesday, 9 March 2021 at 03:08:14 UTC, Paul Backus wrote:
> else static if (__traits(compiles, f(1, 2)))
>     f(2);

Typo; this should of course say `f(1, 2)`.
March 09, 2021
On Tuesday, 9 March 2021 at 03:08:14 UTC, Paul Backus wrote:
> Yes, it's possible. For example:
>
> template fun(T) {

It'd be nice if we could at least get the arity of a template, then perhaps speculatively instantiate it and reflect on the eponymous function then.

(that's a lot of jargon lol but like some kind of reflection over template parameters is definitely possible... just the compiler doesn't offer any facilities for it at all. Then once you're past that layer you can start trying other things.)
March 09, 2021
On Tuesday, 9 March 2021 at 03:08:14 UTC, Paul Backus wrote:
> If you know the arguments you want to call the function with, the easiest way to check if you can do it is with __traits(compiles):
>
> static if (__traits(compiles, f(1)))
>     f(1);
> else static if (__traits(compiles, f(1, 2)))
>     f(2);

Yes, I can do some magic with this.

>> I understand that types are not available since they are undefined until instantiation. But why can't I get argument count? Is it possible that it can change during instantiation?
>
> Yes, it's possible. For example:
>
> template fun(T) {
>     static if (is(T == int)) {
>         void fun(T a, T b) {
>             /* ... */
>         }
>     } else {
>         void fun(T a) {
>             /* ... */
>         }
>     }
> }
>
> Of course, code like this is rare in practice, but because it's technically possible, the compiler can't assume *anything* about a template function before it's been instantiated.

In case of function template this is possible but the original question was about lambdas. There is no way that lambda can change number of parameters during instantiation, am I right?
March 09, 2021
On Tuesday, 9 March 2021 at 14:22:44 UTC, Andrey Zherikov wrote:
> In case of function template this is possible but the original question was about lambdas. There is no way that lambda can change number of parameters during instantiation, am I right?

Yes, you're correct. The issue is that the compiler currently can't tell the difference between a template lambda and a non-lambda function template.

This could be changed, but having a language feature that works for lambdas but not for named functions is probably not such a great idea.