Bill Baxter wrote:
On Thu, Nov 12, 2009 at 1:00 PM, Walter Bright
<newshou...@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

Reply via email to