Jump to page: 1 2
Thread overview
Showing a user specified error message when no overloads match
Jul 25, 2014
Marc Schütz
Jul 26, 2014
Trass3r
Jul 26, 2014
Marc Schütz
Jul 27, 2014
H. S. Teoh
Jul 27, 2014
Marc Schütz
Jul 27, 2014
monarch_dodra
Jul 29, 2014
Vlad Levenfeld
Jul 29, 2014
H. S. Teoh
Jul 29, 2014
Vlad Levenfeld
Jul 29, 2014
H. S. Teoh
Jul 29, 2014
Artur Skawina
Jul 29, 2014
H. S. Teoh
July 25, 2014
I have a template function with a particular constraint (in this case [1]). When this constraint doesn't match, I want to give the user a suggestion what to do instead.

The only way I know of to do this currently is to relax the template constraints, and adding a `static assert`. However, if there is another function that would match, this would lead to an ambiguity.

Is there a way to trigger the static assert only if there are no other overloads (including members and UFCS functions) that match? Or maybe a solution that only works for this particular case?

[1] https://github.com/D-Programming-Language/phobos/pull/2350
July 26, 2014
Yeah that's the price we pay for the simplicity.
Also most constraints directly or indirectly consist of a complex boolean expressions and you don't get any hint which part failed and why.
July 26, 2014
Hmmm... thinking about it, is this possible?

1. Remove the constraints to match anything.
2. Inside the template, have some construct that enumerates all possible overloads and UFCS functions that are visible at the point of instantiation.
3. If this set contains only the current template, use a static assert to print the message.
4. Otherwise, make the template fail to compile somehow (for example, evaluate a semantically invalid expression), and hope that the compiler will then take the other overloads into consideration (SFINAE).

Could this work?
July 27, 2014
On Sat, Jul 26, 2014 at 05:14:44PM +0000, via Digitalmars-d-learn wrote:
> Hmmm... thinking about it, is this possible?
> 
> 1. Remove the constraints to match anything.
> 2. Inside the template, have some construct that enumerates all possible
> overloads and UFCS functions that are visible at the point of instantiation.
> 3. If this set contains only the current template, use a static assert
> to print the message.
> 4. Otherwise, make the template fail to compile somehow (for example,
> evaluate a semantically invalid expression), and hope that the
> compiler will then take the other overloads into consideration
> (SFINAE).
> 
> Could this work?

D does not have SFINAE.

This has been discussed before. I proposed the following solution:

- Sig constraints should match all types that the function *logically*
  accepts -- even if the current implementation does not support some of
  said types.

- In the function body, use a `static if` chain to implement
  specializations.

- In the final else clause, do a static assert(0) with a user-friendly
  error message.


T

-- 
In order to understand recursion you must first understand recursion.
July 27, 2014
On Sunday, 27 July 2014 at 00:43:40 UTC, H. S. Teoh via Digitalmars-d-learn wrote:
> On Sat, Jul 26, 2014 at 05:14:44PM +0000, via Digitalmars-d-learn wrote:
>> Hmmm... thinking about it, is this possible?
>> 
>> 1. Remove the constraints to match anything.
>> 2. Inside the template, have some construct that enumerates all possible
>> overloads and UFCS functions that are visible at the point of instantiation.
>> 3. If this set contains only the current template, use a static assert
>> to print the message.
>> 4. Otherwise, make the template fail to compile somehow (for example,
>> evaluate a semantically invalid expression), and hope that the
>> compiler will then take the other overloads into consideration
>> (SFINAE).
>> 
>> Could this work?
>
> D does not have SFINAE.

http://dlang.org/templates-revisited.html says otherwise.

But thinking about it, I've never seen it used anywhere, nor used it myself, and even the examples in the linked article under the SFINAE section use `is` expressions instead...

>
> This has been discussed before. I proposed the following solution:
>
> - Sig constraints should match all types that the function *logically*
>   accepts -- even if the current implementation does not support some of
>   said types.
>
> - In the function body, use a `static if` chain to implement
>   specializations.
>
> - In the final else clause, do a static assert(0) with a user-friendly
>   error message.

But this prevents other people from providing overloads in their own modules :-(
July 27, 2014
On Sunday, 27 July 2014 at 08:40:42 UTC, Marc Schütz wrote:
> On Sunday, 27 July 2014 at 00:43:40 UTC, H. S. Teoh via Digitalmars-d-learn wrote:
>> On Sat, Jul 26, 2014 at 05:14:44PM +0000, via Digitalmars-d-learn wrote:
>>> Hmmm... thinking about it, is this possible?
>>> 
>>> 1. Remove the constraints to match anything.
>>> 2. Inside the template, have some construct that enumerates all possible
>>> overloads and UFCS functions that are visible at the point of instantiation.
>>> 3. If this set contains only the current template, use a static assert
>>> to print the message.
>>> 4. Otherwise, make the template fail to compile somehow (for example,
>>> evaluate a semantically invalid expression), and hope that the
>>> compiler will then take the other overloads into consideration
>>> (SFINAE).
>>> 
>>> Could this work?
>>
>> D does not have SFINAE.
>
> http://dlang.org/templates-revisited.html says otherwise.
>
> But thinking about it, I've never seen it used anywhere, nor used it myself, and even the examples in the linked article under the SFINAE section use `is` expressions instead...

It's poorly worded: "is" is what functionally replaces the SFINAE functionality.

>>
>> This has been discussed before. I proposed the following solution:
>>
>> - Sig constraints should match all types that the function *logically*
>>  accepts -- even if the current implementation does not support some of
>>  said types.
>>
>> - In the function body, use a `static if` chain to implement
>>  specializations.
>>
>> - In the final else clause, do a static assert(0) with a user-friendly
>>  error message.
>
> But this prevents other people from providing overloads in their own modules :-(

Not quite: It prevents users from providing "un-ambigous" overloads. But arguably, it's a good thing.

I've argued before against the "over use" of template constraints for type validation. Their initial use was really to restrict the "logical types" that can be used, and help with template overload. Not for validation. If the type then has some "flaw", that's where the static ifs come in.

This as a several advantages:
- Cleaner interface (*cough* http://dlang.org/phobos/std_algorithm.html#sort *cough*)
- Better error messages in case of non match ("The provided range can not be stable sorted, because its elements are not lvalues").
- Prevents "overload" turning into a hijack. You can add your overload (without ambiguity) if it works on types completely un-related. That won't be ambiguous. But if you are adding a very specific variant, it might be better that your function can't be accidentally called, and create surprises later.
July 29, 2014
opDispatch behaves as though it has SFINAE. When something fails in the definition (like I am having now, some of the symbols I used in it hadn't been imported) there won't ever be an error message, I just get "Error: no property 'bar' for type 'Foo'"

In one case I had to use static ifs and pragmas and static assert(0) to get error messages out of opDispatch because static assert messages were being suppressed.

Its very frustrating.
July 29, 2014
On Tue, Jul 29, 2014 at 03:34:22AM +0000, Vlad Levenfeld via Digitalmars-d-learn wrote:
> opDispatch behaves as though it has SFINAE. When something fails in the definition (like I am having now, some of the symbols I used in it hadn't been imported) there won't ever be an error message, I just get "Error: no property 'bar' for type 'Foo'"
> 
> In one case I had to use static ifs and pragmas and static assert(0) to get error messages out of opDispatch because static assert messages were being suppressed.
> 
> Its very frustrating.

You're right, opDispatch behaves like SFINAE. I've had trouble debugging it before, because when it works, it works very well, but when you accidentally make a typo, it just "disappears" -- you get an error that the property is missing, but the actual error inside opDispatch has been gagged and it's almost impossible to get at the actual error message.

Maybe we should file an enhancement bug to improve error reporting for opDispatch.


T

-- 
Time flies like an arrow. Fruit flies like a banana.
July 29, 2014
On 07/29/14 17:45, H. S. Teoh via Digitalmars-d-learn wrote:
> You're right, opDispatch behaves like SFINAE. I've had trouble debugging it before, because when it works, it works very well, but when you accidentally make a typo, it just "disappears" -- you get an error that the property is missing, but the actual error inside opDispatch has been gagged and it's almost impossible to get at the actual error message.

D's overloaded operators are (usually) normal (templated) functions, you
can use `a.opDispatch!"blah"` instead of `a.blah` to see what's wrong.

What's really nasty is the way phobos handles `toString` - if that method fails to compile then you get a usually not very helpful default, and no warning that something is wrong. It's easy to break `toString` w/o noticing anything. Figuring out later what exactly broke can be "interesting". Still doable via the above mentioned trick, but you'll need to create a mock `sink` etc.

artur
July 29, 2014
On Tue, Jul 29, 2014 at 06:57:00PM +0200, Artur Skawina via Digitalmars-d-learn wrote:
> On 07/29/14 17:45, H. S. Teoh via Digitalmars-d-learn wrote:
> > You're right, opDispatch behaves like SFINAE. I've had trouble debugging it before, because when it works, it works very well, but when you accidentally make a typo, it just "disappears" -- you get an error that the property is missing, but the actual error inside opDispatch has been gagged and it's almost impossible to get at the actual error message.
> 
> D's overloaded operators are (usually) normal (templated) functions,
> you can use `a.opDispatch!"blah"` instead of `a.blah` to see what's
> wrong.

Good idea! Though it's still limited when the opDispatch call is buried under several layers of templated generic functions. The problem is, sometimes you don't even know that the problem is inside opDispatch because failure causes it to default to something else (that generates an unrelated error message), and the actual error message is gagged. Then once you do narrow it down, it isn't always so easy to get at the code that calls opDispatch, since it could be quite deep inside generic functions. Once you pinpoint it, though, your idea is pretty good -- invoke it directly so that the errors are not gagged.


> What's really nasty is the way phobos handles `toString` - if that method fails to compile then you get a usually not very helpful default, and no warning that something is wrong. It's easy to break `toString` w/o noticing anything. Figuring out later what exactly broke can be "interesting". Still doable via the above mentioned trick, but you'll need to create a mock `sink` etc.
[...]

Mock sinks are very easy, because a delegate is a sink. :-)

	import std.range;
	int delegate(const(char)[]) dg;
	static assert(isOutputRange!(typeof(dg), const(char)[])); // passes

Which makes it very handy to insert arbitrary debugging code for toString via dg.formattedWrite("%s", obj).


T

-- 
"I'm running Windows '98."
"Yes."
"My computer isn't working now."
"Yes, you already said that."
-- User-Friendly
« First   ‹ Prev
1 2