| Thread overview | |||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
November 12, 2009 Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Denis Koroskin | 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Don | > > 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 Re: Getting the error from __traits(compiles, ...) | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Don | 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
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply