Jump to page: 1 2 3
Thread overview
Getting the error from __traits(compiles, ...)
Nov 12, 2009
Bill Baxter
Nov 12, 2009
Walter Bright
Nov 12, 2009
Walter Bright
Nov 12, 2009
Denis Koroskin
Nov 12, 2009
Walter Bright
Nov 12, 2009
Bill Baxter
Nov 13, 2009
Yigal Chripun
Nov 13, 2009
grauzone
Nov 13, 2009
Yigal Chripun
Nov 13, 2009
grauzone
Nov 13, 2009
Don
Nov 13, 2009
Justin Johansson
Nov 13, 2009
bearophile
Nov 13, 2009
Bill Baxter
Nov 13, 2009
bearophile
Nov 13, 2009
Bill Baxter
Nov 13, 2009
Bill Baxter
Nov 13, 2009
Yigal Chripun
Nov 13, 2009
Bill Baxter
Nov 14, 2009
Yigal Chripun
Nov 14, 2009
Don
Nov 14, 2009
Yigal Chripun
November 12, 2009
We can pretty much already use __traits(compiles,{...}) to implement
static interface checking.

template checkInterface(T) {
    enum bool checkInterface =
    __traits(compiles,
    {
       T x;
       // some code exercising various aspects of the interface
    });
}

The main problem with this is that a static "implements interface" check is done like so:

   static assert(checkInterface!(myType));

And all it tells you is a single yes or no.  It would help in debugging if you could somehow get the reason for the failure.

So how to do it?  All that comes to mind is something like the evil
Errno from C.  A global that gets set by the last failure.
Let's call it __errmsg, but it could be a pragma, or a __traits thing.
 If you had that then you could write this:

   assertImplements!(checkInterface!(myType));

With:
template assertImplements(bool v)
{
       static if (!v) {
            pragma(msg, __errmsg);
            static assert(false);
       }
}

There are lots of ways you could provide such a concept-checker, but they all require the basic ability to get the reason for the __traits(compiles) failure.

Any other thoughts about how to get the failure info?   This is probably the main complaint against __traits(compiles), that there's no way to find out what went wrong if the code doesn't compile.  Often it can just be a typo.  I know I've spent plenty of time looking at static if(__traits(compiles, ...)) checks that weren't working only to discover I switched an x for a y somewhere.  Or passed the wrong number of arguments.

--bb
November 12, 2009
Bill Baxter wrote:
> Any other thoughts about how to get the failure info?   This is
> probably the main complaint against __traits(compiles), that there's
> no way to find out what went wrong if the code doesn't compile.  Often
> it can just be a typo.  I know I've spent plenty of time looking at
> static if(__traits(compiles, ...)) checks that weren't working only to
> discover I switched an x for a y somewhere.  Or passed the wrong
> number of arguments.

I agree it's a problem. Perhaps we can do:

   __traits(compiles_or_msg, ...)

which would print the error messages, at least making it easier to track down.
November 12, 2009
Walter Bright wrote:
> Bill Baxter wrote:
>> Any other thoughts about how to get the failure info?   This is
>> probably the main complaint against __traits(compiles), that there's
>> no way to find out what went wrong if the code doesn't compile.  Often
>> it can just be a typo.  I know I've spent plenty of time looking at
>> static if(__traits(compiles, ...)) checks that weren't working only to
>> discover I switched an x for a y somewhere.  Or passed the wrong
>> number of arguments.
> 
> I agree it's a problem. Perhaps we can do:
> 
>    __traits(compiles_or_msg, ...)
> 
> which would print the error messages, at least making it easier to track down.

Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!
November 12, 2009
On Fri, 13 Nov 2009 00:00:42 +0300, Walter Bright <newshound1@digitalmars.com> wrote:

> Walter Bright wrote:
>> Bill Baxter wrote:
>>> Any other thoughts about how to get the failure info?   This is
>>> probably the main complaint against __traits(compiles), that there's
>>> no way to find out what went wrong if the code doesn't compile.  Often
>>> it can just be a typo.  I know I've spent plenty of time looking at
>>> static if(__traits(compiles, ...)) checks that weren't working only to
>>> discover I switched an x for a y somewhere.  Or passed the wrong
>>> number of arguments.
>>  I agree it's a problem. Perhaps we can do:
>>     __traits(compiles_or_msg, ...)
>>  which would print the error messages, at least making it easier to track down.
>
> Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!

He was asking for a string so that it would be possible to parse and/or output. More like:

enum s = __traits(compiles_or_msg, ...);
static if (s.length == 0) {
   // no error
} else {
   // do stuff with error message
}
November 12, 2009
On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright <newshound1@digitalmars.com> wrote:
> Walter Bright wrote:
>>
>> Bill Baxter wrote:
>>>
>>> Any other thoughts about how to get the failure info?   This is probably the main complaint against __traits(compiles), that there's no way to find out what went wrong if the code doesn't compile.  Often it can just be a typo.  I know I've spent plenty of time looking at static if(__traits(compiles, ...)) checks that weren't working only to discover I switched an x for a y somewhere.  Or passed the wrong number of arguments.
>>
>> I agree it's a problem. Perhaps we can do:
>>
>>   __traits(compiles_or_msg, ...)
>>
>> which would print the error messages, at least making it easier to track down.
>
> Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and replace it with ..., and you'll get the message!

Maybe that is enough combined with a const code snippet

enum code = q{
        R r;             // can define a range object
        if (r.empty) {}  // can test for empty
        r.popFront;          // can invoke next
        auto h = r.front; // can get the front of the range
}
static if (__traits(compiles, mixin(code))) {
    mixin(code);
}
else {
    pragma(msg, "Unable to instantiate code for type
T=`"~T.stringof~"`:\n "~ code);
    pragma(msg, "Compiler reports:" );
    mixin(code);
}

But I was really hoping for a separation of Interface definition and
Interface verification.  With the above you'll have to have two
templates for every interface,  like  isForwardRange!(T) (evals to
bool)  and assertIsForwardRange!(T)  (reports the compiler error or is
silent).   Hmm.... unless

template assertIsInputRange(T, bool noisy=true) {
     enum code = q{
             R r;             // can define a range object
             if (r.empty) {}  // can test for empty
             r.popFront;          // can invoke next
            auto h = r.front; // can get the front of the range
     };
     static if (!__traits(compiles, mixin(code))) {
        static if (noisy) {
             pragma(msg, "Type T=`"~T.stringof~"` doesn't support
interface:\n "~ code);
             pragma(msg, "Compiler reports:" );
        }
        mixin(code);
     }
}

template isInputRange(T) {
     enum bool isInputRange = __traits(compiles, assertIsInputRange!(T, false));
}

And then we could wrap the whole shebang in a fancy code-generating string mixin and define things like the above using:

mixin(DefineInterface(
    "InputRange",
    q{
             R r;             // can define a range object
             if (r.empty) {}  // can test for empty
             r.popFront;          // can invoke next
            auto h = r.front; // can get the front of the range
     }));

mixin(DefineInterface!(assertIsInputRange)(
    "ForwardRange",
     q{
        R r1;
        R r2 = r1;           // can copy a range object
      })));

Writing DefineInterface is left as an exercise for the reader. :-)
But it looks do-able.
And DefineInterface could take a variadic list of assertIsXXX template
aliases and generate code to check each one.

--bb
November 12, 2009
Denis Koroskin wrote:
> He was asking for a string so that it would be possible to parse and/or output. More like:
> 
> enum s = __traits(compiles_or_msg, ...);
> static if (s.length == 0) {
>    // no error
> } else {
>    // do stuff with error message
> }

Makes sense.
November 13, 2009
Bill Baxter wrote:
> On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright
> <newshound1@digitalmars.com> wrote:
>> Walter Bright wrote:
>>> Bill Baxter wrote:
>>>> Any other thoughts about how to get the failure info?   This is
>>>> probably the main complaint against __traits(compiles), that there's
>>>> no way to find out what went wrong if the code doesn't compile.  Often
>>>> it can just be a typo.  I know I've spent plenty of time looking at
>>>> static if(__traits(compiles, ...)) checks that weren't working only to
>>>> discover I switched an x for a y somewhere.  Or passed the wrong
>>>> number of arguments.
>>> I agree it's a problem. Perhaps we can do:
>>>
>>>   __traits(compiles_or_msg, ...)
>>>
>>> which would print the error messages, at least making it easier to track
>>> down.
>> Eh, scratch that dumb idea. Just remove the __traits(compiles, ...) and
>> replace it with ..., and you'll get the message!
> 
> Maybe that is enough combined with a const code snippet
> 
> enum code = q{
>         R r;             // can define a range object
>         if (r.empty) {}  // can test for empty
>         r.popFront;          // can invoke next
>         auto h = r.front; // can get the front of the range
> }
> static if (__traits(compiles, mixin(code))) {
>     mixin(code);
> }
> else {
>     pragma(msg, "Unable to instantiate code for type
> T=`"~T.stringof~"`:\n "~ code);
>     pragma(msg, "Compiler reports:" );
>     mixin(code);
> }
> 
> But I was really hoping for a separation of Interface definition and
> Interface verification.  With the above you'll have to have two
> templates for every interface,  like  isForwardRange!(T) (evals to
> bool)  and assertIsForwardRange!(T)  (reports the compiler error or is
> silent).   Hmm.... unless
> 
> template assertIsInputRange(T, bool noisy=true) {
>      enum code = q{
>              R r;             // can define a range object
>              if (r.empty) {}  // can test for empty
>              r.popFront;          // can invoke next
>             auto h = r.front; // can get the front of the range
>      };
>      static if (!__traits(compiles, mixin(code))) {
>         static if (noisy) {
>              pragma(msg, "Type T=`"~T.stringof~"` doesn't support
> interface:\n "~ code);
>              pragma(msg, "Compiler reports:" );
>         }
>         mixin(code);
>      }
> }
> 
> template isInputRange(T) {
>      enum bool isInputRange = __traits(compiles, assertIsInputRange!(T, false));
> }
> 
> And then we could wrap the whole shebang in a fancy code-generating
> string mixin and define things like the above using:
> 
> mixin(DefineInterface(
>     "InputRange",
>     q{
>              R r;             // can define a range object
>              if (r.empty) {}  // can test for empty
>              r.popFront;          // can invoke next
>             auto h = r.front; // can get the front of the range
>      }));
> 
> mixin(DefineInterface!(assertIsInputRange)(
>     "ForwardRange",
>      q{
>         R r1;
>         R r2 = r1;           // can copy a range object
>       })));
> 
> Writing DefineInterface is left as an exercise for the reader. :-)
> But it looks do-able.
> And DefineInterface could take a variadic list of assertIsXXX template
> aliases and generate code to check each one.
> 
> --bb

I really wish this was folded into the language by allowing structs to implement interfaces.

interface Range(T) {
  bool empty();
  void popFront();
  T front();
}

struct MyRange(T) : Range!(T) { ... } // checked by compiler

November 13, 2009
Bill Baxter wrote:
> We can pretty much already use __traits(compiles,{...}) to implement
> static interface checking.
> 
> template checkInterface(T) {
>     enum bool checkInterface =
>     __traits(compiles,
>     {
>        T x;
>        // some code exercising various aspects of the interface
>     });
> }
> 
> The main problem with this is that a static "implements interface"
> check is done like so:
> 
>    static assert(checkInterface!(myType));
> 
> And all it tells you is a single yes or no.  It would help in
> debugging if you could somehow get the reason for the failure.
> 
> So how to do it?  All that comes to mind is something like the evil
> Errno from C.  A global that gets set by the last failure.
> Let's call it __errmsg, but it could be a pragma, or a __traits thing.
>  If you had that then you could write this:
> 
>    assertImplements!(checkInterface!(myType));
> 
> With:
> template assertImplements(bool v)
> {
>        static if (!v) {
>             pragma(msg, __errmsg);
>             static assert(false);
>        }
> }
> 
> There are lots of ways you could provide such a concept-checker, but
> they all require the basic ability to get the reason for the
> __traits(compiles) failure.
> 
> Any other thoughts about how to get the failure info?   This is
> probably the main complaint against __traits(compiles), that there's
> no way to find out what went wrong if the code doesn't compile.  Often
> it can just be a typo.  I know I've spent plenty of time looking at
> static if(__traits(compiles, ...)) checks that weren't working only to
> discover I switched an x for a y somewhere.  Or passed the wrong
> number of arguments.
> 
> --bb

I think you might be asking for:

static try {
   xxx;
}
catch( CompilerError[] errors){
   pragma(msg, "Failure in Frobozz!");
   pragma(msg, errors[0].msg);
}
November 13, 2009
> > There are lots of ways you could provide such a concept-checker, but they all require the basic ability to get the reason for the __traits(compiles) failure.
> > 
> > Any other thoughts about how to get the failure info?   This is probably the main complaint against __traits(compiles), that there's no way to find out what went wrong if the code doesn't compile.  Often it can just be a typo.  I know I've spent plenty of time looking at static if(__traits(compiles, ...)) checks that weren't working only to discover I switched an x for a y somewhere.  Or passed the wrong number of arguments.
> > 
> > --bb
> 
> I think you might be asking for:
> 
> static try {
>     xxx;
> }
> catch( CompilerError[] errors){
>     pragma(msg, "Failure in Frobozz!");
>     pragma(msg, errors[0].msg);
> }

This is very interesting.
Given previous/recent discussion about scopes, should there be,
or are there, analogous static scope statements so that one could make
use of scope(exit), scope(failure), et. al. in a static context?

beers,
Justin

November 13, 2009
Don:

> I think you might be asking for:
> 
> static try {
>     xxx;
> }
> catch( CompilerError[] errors){
>     pragma(msg, "Failure in Frobozz!");
>     pragma(msg, errors[0].msg);
> }

Good. I'd like to have a handy&clean way to verify that some static asserts throw inside my unit tests.

Bye,
bearophile
« First   ‹ Prev
1 2 3