Getting the error from __traits(compiles, ...)

Bill Baxter wbaxter at gmail.com
Thu Nov 12 13:44:12 PST 2009


On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright
<newshound1 at 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



More information about the Digitalmars-d mailing list