Comment #0 by snarwin+bugzilla — 2019-09-26T18:50:31Z
import std.traits: isCallable;
struct S { int opCall(T)(T) { return 0; } }
static assert(isCallable!(S.init));
---
The above static assert should pass, but instead fails.
Comment #1 by simen.kjaras — 2019-09-27T06:34:43Z
It's impossible to distinguish between the above case and this:
struct S { enum opCall(T) = true; }
Clearly, the latter is *not* callable.
To illustrate the situation perhaps more clearly:
struct S {
template opCall(T) {
static if (is(T == int)) {
void opCall(T t) {}
} else {
enum opCall = 14;
}
}
}
The above is perfectly valid code, but should isCallable!S return true?
Comment #2 by snarwin+bugzilla — 2019-09-27T15:35:26Z
Seems like this is an issue with the interface, then, not the implementation; the question "is S.init callable" is undecidable, so there's not much use trying to pretend we can answer it.
One possible solution would be a trait like `isCallableWith!(S.init, int)`, that requires the user to specify the argument type(s). That way, we could just check with __traits(compiles) whether the call is valid or not.
Comment #3 by maxsamukha — 2019-09-28T06:27:27Z
(In reply to Paul Backus from comment #2)
> One possible solution would be a trait like `isCallableWith!(S.init, int)`,
> that requires the user to specify the argument type(s). That way, we could
> just check with __traits(compiles) whether the call is valid or not.
That does not take into account rvalue-ness of the arguments:
void foo(ref int) {}
store
static if (isCallableWith!(typeof(1)))
foo(1); // error
isCallableWith would have to accept the actual arguments, (type, rvalue-ness) pairs, or both.
And if one implements isCallableWith for functions, he should also implement isInstantiatableWith for templates. And that is a case for general __traits(matchFunction/matchTemplate, symbol, args) that would return the overloads callable/instantiatable with args. isCallableWith/isInstantiatableWith could be built on that.
Comment #4 by maxsamukha — 2019-09-28T06:29:30Z
(In reply to Max Samukha from comment #3)
> isCallableWith!(typeof(1))
Typo: isCallableWith!(foo, typeof(1))
Comment #5 by snarwin+bugzilla — 2019-09-28T15:30:04Z
(In reply to Max Samukha from comment #3)
> That does not take into account rvalue-ness of the arguments:
>
> void foo(ref int) {}
> store
> static if (isCallableWith!(typeof(1)))
> foo(1); // error
>
> isCallableWith would have to accept the actual arguments, (type,
> rvalue-ness) pairs, or both.
Then perhaps the best solution is to add a note to the documentation of isCallable highlighting its shortcomings, and encourage everyone to instead use __traits(compiles) directly with the actual function call they want to make.
Comment #6 by simen.kjaras — 2020-08-10T07:26:58Z
*** Issue 21137 has been marked as a duplicate of this issue. ***
Comment #7 by simen.kjaras — 2020-08-10T07:40:37Z
It's impossible to solve this in the general case, and even the special cases can't be solved in library code, but a __traits could check if the only member of the template is a callable (potentially also if all possible members would be callables).
Comment #8 by robert.schadek — 2024-12-01T16:35:39Z