Thread overview
Re: Producing nicer template errors in D libraries
Apr 10, 2012
Artur Skawina
Apr 10, 2012
H. S. Teoh
Apr 10, 2012
Artur Skawina
April 10, 2012
On 04/10/12 21:45, H. S. Teoh wrote:
> This produces far more readable error messages when an error happens. But it also requires lots of boilerplate static if's for every function that currently uses isOutputRange in their signature constraint.
> 
> So here's take #2:
> 
> 	void put(T,R)(R range, T data) if (assertIsOutputRange!R) { ... }

> What do y'all think of this idea?


I had to do this in the gtk bindings, otherwise it was pretty
much impossible to figure out what was wrong just from the compiler
errors, when you eg forgot to mark a callback as extern(C).
So now i have template constraints like this one:

   ulong signal_connect(string name:"activate-link", CB/*:signal_activate_link*/)
         (CB cb, void* data=null, ConnectFlags cf=cast(ConnectFlags)0)
         if (is(typeof(cb)==signal_activate_link)||_ttmm!(CB, signal_activate_link)()) {
      return signal_connect_data!()(&this, cast(char*)"activate-link",
      cast(GObject2.Callback)cb, data, null, cf);
   }

and a helper template:

   // Give the user at least a chance to figure out what's wrong when a template
   // constraint doesn't match - print both the found and the expected types.
   bool _ttmm(T, E)() {
      pragma(msg, "\nExpected: '" ~ E.stringof ~"';\n   found: '" ~ T.stringof ~"'.\n");
      return 0;
   }

static assert wasn't necessary.

artur
April 10, 2012
On Tue, Apr 10, 2012 at 10:13:29PM +0200, Artur Skawina wrote: [...]
>    // Give the user at least a chance to figure out what's wrong when a template
>    // constraint doesn't match - print both the found and the expected types.
>    bool _ttmm(T, E)() {
>       pragma(msg, "\nExpected: '" ~ E.stringof ~"';\n   found: '" ~ T.stringof ~"'.\n");
>       return 0;
>    }
> 
> static assert wasn't necessary.
[...]

I like this idea too. In fact, it might be better than having a catchall overload with a static assert, because here you can actually see what constraints were tried and failed for each failed sig constraint. May make bugs easier to locate if the relevant sig constraints are very complex. (Plus, in the latter case, it's not so easy to write a catch-all constraint due to D's (deliberate?) lack of a default-matching mechanism for templates.)

The only complaint, though, is, wouldn't this produce lots of noise even with successful compiles? I assume the pragma will run even if there's a later template match after a series of failed overloads.


T

-- 
Ignorance is bliss... but only until you suffer the consequences!
April 10, 2012
On 04/10/12 23:08, H. S. Teoh wrote:
> On Tue, Apr 10, 2012 at 10:13:29PM +0200, Artur Skawina wrote: [...]
>>    // Give the user at least a chance to figure out what's wrong when a template
>>    // constraint doesn't match - print both the found and the expected types.
>>    bool _ttmm(T, E)() {
>>       pragma(msg, "\nExpected: '" ~ E.stringof ~"';\n   found: '" ~ T.stringof ~"'.\n");
>>       return 0;
>>    }
>>
>> static assert wasn't necessary.
> [...]
> 
> I like this idea too. In fact, it might be better than having a catchall overload with a static assert, because here you can actually see what constraints were tried and failed for each failed sig constraint. May make bugs easier to locate if the relevant sig constraints are very complex. (Plus, in the latter case, it's not so easy to write a catch-all constraint due to D's (deliberate?) lack of a default-matching mechanism for templates.)
> 
> The only complaint, though, is, wouldn't this produce lots of noise even with successful compiles? I assume the pragma will run even if there's a later template match after a series of failed overloads.

Yes - this isn't a problem in my case because the templates are also specialized
for each signal. So it only has to deal with trying to register a signal
with a wrong callback signature; something that can be surprisingly hard to figure
out as the compiler won't tell you what it didn't like about the arguments.
static assert wasn't necessary - in this case.

artur