Thread overview
Checking function callability in D1 (aka static try-catch)
Dec 07, 2008
Bill Baxter
Dec 07, 2008
Christian Kamm
Dec 07, 2008
Bill Baxter
December 07, 2008
This topic came up (again) fairly recently, and a solution was proposed, but I found a case where that solution doesn't work.

The objective is to do a static check to see if something is callable with particular argument types, and if so call it.

The proposed solution was to use '.init' like so:

    static if (is(typeof( some_function(ArgType.init) ))) {
        // call some_function , or make an alias to it or whatever
    }

But this doesn't always work if some function takes a ref argument. For instance if ArgType is 'float', and some_function's signature is:

    void some_function(ref float);

Then it will fail.

The ugly workaround I found is to use this:

    static if (is(typeof( some_function(cast(ArgType)Object.init) ))) { ... }

First it's ugly, and second I'm not sure that really should work anyway.  I don't think a 100% clever compiler would allow using Object.init as a ref parameter even with a cast.

Any other ideas for how to test if a function is callable?  or is that the best we can do?

Note that checking the type of the function itself is not acceptable, because this is static duck-typing here -- you don't really care if it's a function or delegate or a struct that implements opCall, as long as you can call it.   And also you don't really care if the argument is exactly ArgType, as long as the argument can be implicitly converted from ArgType.

--bb
December 07, 2008
> This topic came up (again) fairly recently, and a solution was proposed, but I found a case where that solution doesn't work.
> 
> The objective is to do a static check to see if something is callable with particular argument types, and if so call it.
> 
> The proposed solution was to use '.init' like so:
> 
>     static if (is(typeof( some_function(ArgType.init) ))) {
>         // call some_function , or make an alias to it or whatever
>     }
> 
> But this doesn't always work if some function takes a ref argument.

I didn't follow the original discussion, but what about:

template makeValue(T...) { T makeValue; }
template isCallable(alias s, ArgTy...) {
  const bool isCallable = is(typeof(s(makeValue!(ArgTy))));
}

December 07, 2008
On Sun, Dec 7, 2008 at 7:08 PM, Christian Kamm <kamm-incasoftware@removethis.de> wrote:
>> This topic came up (again) fairly recently, and a solution was proposed, but I found a case where that solution doesn't work.
>>
>> The objective is to do a static check to see if something is callable with particular argument types, and if so call it.
>>
>> The proposed solution was to use '.init' like so:
>>
>>     static if (is(typeof( some_function(ArgType.init) ))) {
>>         // call some_function , or make an alias to it or whatever
>>     }
>>
>> But this doesn't always work if some function takes a ref argument.
>
> I didn't follow the original discussion, but what about:
>
> template makeValue(T...) { T makeValue; }
> template isCallable(alias s, ArgTy...) {
>  const bool isCallable = is(typeof(s(makeValue!(ArgTy))));
> }

That's pretty good.  Thanks.
Only problem is that it seems isCallable will generate an error.
Seems an is() is needed at the call site.

This seems to do the trick:

private template makeValue(T...) { T makeValue; }
private template Callable(alias s, ArgTy...) {
    static if (is(typeof(s(makeValue!(ArgTy))) FType)) {
        alias FType Callable;
    }
    else {
        static assert(false,
                      s.stringof ~ " not callable with args "~ArgTy.stringof);
    }
}
...
static if (is(Callable!(some_function, ArgType))) { ... }

--bb