digitalmars.D.learn - better assertions and __FILE__
- Bruce Adams (36/36) Aug 15 2007 Hi,
- Bruce Adams (31/63) Aug 16 2007 Here are a few more things I've learned on my travels around the various...
- Bruce Adams (49/49) Aug 16 2007 I have given in and raised this as an issue:
- Lutger (20/24) Aug 17 2007 It's possible to use CTFE with string mixins to achieve this. I have
- Lutger (2/2) Aug 17 2007 Also note that it is necessary to 'public import std.metastrings' if
- Bruce Adams (34/68) Aug 19 2007 I'd rather live with A) - than without the option to do this at all.
- Lutger (29/45) Aug 20 2007 This is almost correct, see below.
- Bruce Adams (53/53) Aug 20 2007 Right, thanks. I'm nearly there.
- Bruce Adams (28/97) Aug 21 2007 Hi all,
- Chris Nicholson-Sauls (26/130) Aug 22 2007 Try this...
- Bruce Adams (15/47) Aug 24 2007 That doesn't work, but this does:
- Bruce Adams (6/61) Aug 16 2007 Okay. So having realised my first idea is not possible with current D (p...
- Frank Benoit (2/4) Aug 16 2007 "foo"[]
- Bruce Adams (13/18) Aug 16 2007 Thanks. Where is that piece of syntactic sugar documented?
- Deewiant (5/13) Aug 17 2007 http://www.digitalmars.com/d/arrays.html
Hi, Another FAQ item I couldn't find. I'm looking to implement some wrappers around assert as assert is too primative and doesn't give me enough information. I want to go down the xUnit path E.g. in the code: assertEqual(foo,bar); leading to output: Equality assertion failed in file.cpp at line 10 Expected Value: 10 Actual Value: 5 I got as far as: void assertEqual(Type)(Type actual_, Type expected_, char[] file_, uint line_) { if (actual_ != expected_) { writefln("Equality assertion failed"); writefln("actual: '", actual_, "'"); writefln("expected: '", expected_, "'"); _d_assert(file_,line_); } } This lets me use: assertEqual!(string)("foo","bar",cast(char[])(__FILE__),__LINE__); Where I'm having trouble is getting __FILE__ and __LINE__ added (presumably using a mixin) so I can avoid the cruft. As a side issue how do I get string constants implicitly converted to char[] or better yet string. In my example above assertEqual("foo","bar",cast(char[])(__FILE__),__LINE__); also works but only because "foo".length == "bar".length // broken! assertEqual("foo","bar2",cast(char[])(__FILE__),__LINE__); Final question to prove I'm a noob. In std.asserterror shouldn't _d_assert(char[] file_,uint line_); actually be: _d_assert(string file_,uint line_); Regards, Bruce.
Aug 15 2007
Bruce Adams Wrote:Hi, Another FAQ item I couldn't find. I'm looking to implement some wrappers around assert as assert is too primative and doesn't give me enough information. I want to go down the xUnit path E.g. in the code: assertEqual(foo,bar); leading to output: Equality assertion failed in file.cpp at line 10 Expected Value: 10 Actual Value: 5 I got as far as: void assertEqual(Type)(Type actual_, Type expected_, char[] file_, uint line_) { if (actual_ != expected_) { writefln("Equality assertion failed"); writefln("actual: '", actual_, "'"); writefln("expected: '", expected_, "'"); _d_assert(file_,line_); } } This lets me use: assertEqual!(string)("foo","bar",cast(char[])(__FILE__),__LINE__); Where I'm having trouble is getting __FILE__ and __LINE__ added (presumably using a mixin) so I can avoid the cruft.Here are a few more things I've learned on my travels around the various D web-pages. __FILE__ & __LINE__ are expanded by the tokeniser not the parser. This implies that they would not be available for use in mixins as they will already have been expanded. This is confirmed with a quick test. So what I'm trying to do is not currently possible. I have seen two proposals for improvements to D bandied around. 1) "context" functions // does not exist void context(__line, __file) assertEqual(int a, int b) { if (a != b) { writefln("equality assertion failed"); writefln("expected: ",a); writefln("actual: ",b); _d_assert(__line,__file); } } 2) Access to the call stack through "trace" something like: import std.trace; // does not exist yet void assertEqual(int a, int b) { if (a != b) { writefln("equality assertion failed"); writefln("expected: ",a); writefln("actual: ",b); StackFrame callerFrame = getStack().up(); uint callingLine = callerFrame.getLine(); char[] callingFile = callerFrame.getFile(); _d_assert(callingLine, callingFile); } } I think the latter has more appeal and more support. You can use it for generating stack traces and core dumps for example. However, I have no idea of what status these ideas are at. There's nothing in the issue tracking system. I'm not sure if that's the right place for frivolous feature requests. Has anyone heard anything on the grapevine? Are there language development roadmap and feature request pages knocking around the web somewhere that I've yet to find? Regards, Bruce.
Aug 16 2007
I have given in and raised this as an issue: http://d.puremagic.com/issues/show_bug.cgi?id=1425 duplicated below for discussion purposes: For improved support for assertions, logging code and stack traces it would be useful to have means of identifying the calling frame of reference. This would also have the advantage of rendering __LINE__ and its ilk redundant. For example it is not currently possible to implement a cppunit like assertEqual function. The best I can achieve is: void assertEqual(Type)(Type actual_, Type expected_, char[] file_, uint line_) { if (actual_ != expected_) { writefln("Equality assertion failed"); writefln("actual: '", actual_, "'"); writefln("expected: '", expected_, "'"); } _d_assert(file_,line_); } Which has to be used as: assertEqual!(string)("foo","bar",cast(char[])(__FILE__),__LINE__); It should be possible to achieve a more natural syntax like: assertEqual("foo","bar"); Several proposals seem have been made on the newsgroups but none has been raised as a request here. I personally think the best approach is to have a library function stackFrame[] callStack = std.trace(); By default the calling frame would be something like: class stackFrame { stackFrame* parent; object[] arguments; } When functions request source identification the compiler would have to include mapping information so its not a pure library issue. E.g class SourceStackFrame: public StackFrame { // inherited // stackFrame* parent; // object[] arguments; uint parentLine; string* parentFile; string* parentFunction; } I imagine two ways of creating such a structure. Use of an analogue to __FILE__ e.g. the so called "context" keyword would tell the compiler to use the special stack frame for a specific call only. A command line switch could tell the compiler to always use the source stack frame format - with resulting code bloat.
Aug 16 2007
Bruce Adams wrote:Here are a few more things I've learned on my travels around the various D web-pages. __FILE__ & __LINE__ are expanded by the tokeniser not the parser. This implies that they would not be available for use in mixins as they will already have been expanded. This is confirmed with a quick test. So what I'm trying to do is not currently possible.It's possible to use CTFE with string mixins to achieve this. I have whipped up something quick, ugly as hell but could be improved using std.metastrings.Format: char[] assertEqual(char[] actual, char[] expected) { return `if (` ~ actual ~ `!=` ~ expected ~ `) { ` `writefln("Equality assertion failed in " ~ __FILE__ ~ " at line " ~ std.metastrings.ToString!(__LINE__));` `writefln("expected value: '", ` ~ expected ~ `, "'");` `writefln("actual value: '", ` ~ actual ~ `, "'");` `assert(false);}`; } used as: mixin(assertEqual("foo","bar")); If you can live with a) having to use 'mixin', b) ugliness of compile time string manipulation and perhaps c) sometimes hard to follow error messages, you can do most of anything you want with CTFE and string mixins. I think if one would make work of this most of the tediousness could be solved with some boilerplate code.
Aug 17 2007
Also note that it is necessary to 'public import std.metastrings' if this code is used across modules, due to the mixin thing.
Aug 17 2007
Lutger Wrote:Bruce Adams wrote:I'd rather live with A) - than without the option to do this at all. B) is better than it was in C++. I'm already struggling with C) using DMD, if you'll pardon the pun. I can see from using a variant of your example that is possible now. I forgot we had CTFE. I was surprised it allows arbitrary strings to be compiled as I though mxins were supposed to use code segments that have been parsed already. Anyway, I'm still struggling a bit with it. My main problem can be boiled down to the following example. char[] int2str(int foo_) { return std.metastrings.ToString!(foo_); } Fails to compile with c:\dmd\bin\..\src\phobos\std\metastrings.d(102): Error: expression cast(long)foo _ is not a valid template value argument c:\dmd\bin\..\src\phobos\std\metastrings.d(85): Error: expression cast(long)foo_ < 0L is not constant or does not evaluate to a bool Without the shriek it also fails: char[] int2str(int foo_) { return std.metastrings.ToString(foo_); } dmd.exe -c UnitTest.d -ofUnitTest.do -cov c:\dmd\bin\..\src\phobos\std\metastrings.d(74): template std.metastrings.ToStrin g(ulong U) is not a function template UnitTest.d(124): template std.metastrings.ToString(ulong U) cannot deduce templa te function from argument types (int) UnitTest.d(124): Error: cannot implicitly convert expression ((ToString(ulong U) )(foo_)) of type int to char[] Its not obvious to me what I'm doing wrong. Incidentally is there a way of specifying you only want a function to be evaluated at compile time? Perhaps the static keyword? My plan when this kind of thing comes up is to have one function for the run-time portion and a seperate code generator for the compile time stuff. Regards, Bruce.Here are a few more things I've learned on my travels around the various D web-pages. __FILE__ & __LINE__ are expanded by the tokeniser not the parser. This implies that they would not be available for use in mixins as they will already have been expanded. This is confirmed with a quick test. So what I'm trying to do is not currently possible.It's possible to use CTFE with string mixins to achieve this. I have whipped up something quick, ugly as hell but could be improved using std.metastrings.Format: char[] assertEqual(char[] actual, char[] expected) { return `if (` ~ actual ~ `!=` ~ expected ~ `) { ` `writefln("Equality assertion failed in " ~ __FILE__ ~ " at line " ~ std.metastrings.ToString!(__LINE__));` `writefln("expected value: '", ` ~ expected ~ `, "'");` `writefln("actual value: '", ` ~ actual ~ `, "'");` `assert(false);}`; } used as: mixin(assertEqual("foo","bar")); If you can live with a) having to use 'mixin', b) ugliness of compile time string manipulation and perhaps c) sometimes hard to follow error messages, you can do most of anything you want with CTFE and string mixins. I think if one would make work of this most of the tediousness could be solved with some boilerplate code.
Aug 19 2007
Bruce Adams wrote: ...I can see from using a variant of your example that is possible now. I forgot we had CTFE. I was surprised it allows arbitrary strings to be compiled as I though mxins were supposed to use code segments that have been parsed already. Anyway, I'm still struggling a bit with it. My main problem can be boiled down to the following example. char[] int2str(int foo_) { return std.metastrings.ToString!(foo_); }This is almost correct, see below.char[] int2str(int foo_) { return std.metastrings.ToString(foo_); }std.metastrings.ToString is a template, which must be instanciated with !, so this is not correct. My example was bad, I'm sorry, it didn't break because I only used these __LINE__ and __FILE__ tokens. At the very bottom of http://www.digitalmars.com/d/function.html the reason is explained: "Any functions that execute at compile time must also be executable at run time. The compile time evaluation of a function does the equivalent of running the function at run time. " For int2str, this is not the case. If you only need compile time execution, you can rewrite these functions as templates. For my original example: template assertEqual(char[] actual, char[] expected) { const char[] assertEqual = `if (` ~ actual ~ `!=` ~ expected ~ `) { ` `writefln("Equality assertion failed in " ~ __FILE__ ~ " at line " ~ std.metastrings.ToString!(__LINE__));` `writefln("expected value: '", ` ~ expected ~ `, "'");` `writefln("actual value: '", ` ~ actual ~ `, "'");` `assert(false);}`; } mixin(assertEqual!("foo","bar"));Incidentally is there a way of specifying you only want a function to be evaluated at compile time? Perhaps the static keyword? My plan when this kind of thing comes up is to have one function for the run-time portion and a seperate code generator for the compile time stuff.See the eval template also at http://www.digitalmars.com/d/function.html To determine whether a parameter is a constant or not (for dispatching), perhaps one could use is-expressions to try to take the adress: is( typeof(&arg)). That should evaluate to false for constants (at compile time), but true for run-time parameters. Haven't tested it though.
Aug 20 2007
Right, thanks. I'm nearly there. Here's where I'm at: // for example only void assertWrapper(char[] file_, uint line_) { _d_assert(file_,line_); } // the run-time assert part void assertEqual(Type)(Type actual_, Type expected_, char[] file_, uint line_) { if (actual_ != expected_) { writefln("Equality assertion failed"); writefln("actual: '", actual_, "'"); writefln("expected: '", expected_, "'"); } assertWrapper(file_,line_); } const char[] assertEqual4(const char[] actualVar_, const char[] expectedVar_) { return "assertEqual!(int)("[] ~actualVar_ ~","[] ~expectedVar_ ~",cast(char[])(__FILE__),__LINE__);"[]; } const char[] assertEqual5(T)(const char[] actualVar_, const char[] expectedVar_) { return "assertEqual!(" ~typeof(T).stringof ~")("[] ~actualVar_ ~","[] ~expectedVar_ ~",cast(char[])(__FILE__),__LINE__);"[]; } unittest { // declare at run-time const int var3 = 1; const int var4 = 2; int var5 = 1; int var6 = 2; // okay - but must give name of variables writefln(assertEqual4("var5"[],"var6"[])); mixin(assertEqual4("var5"[],"var6"[])); // must provide name and type mixin(assertEqual5!(int)("var5"[],"var6"[])); // must provide name and type mixin(assertEqual5!(typeof(var5))(var5.stringof,var6.stringof)); } I reckon "mixin(assertEqual(var5,var6));" should be possible but I haven't quite figured it out yet. Regards, Bruce. Lutger Wrote: [Some helpful stuff]
Aug 20 2007
Hi all, Still stuck. How do I get the template to include the name of a run-time variable at compile-time? Please somebody put me out of my misery. template assertEqual8(actual_, expected_) { const char[] assertEqual8(actual_ a_, expected_ e_) { return "mixin(assertEqual5!("[] ~actual_.stringof ~")("[] ~a_.stringof ~","[] ~e_.stringof ~"));"[]; } } alias assertEqual8!(int, int) assertEqual9; unittest { // all fine // writes: "mixin(assertEqual5!(int)(a_,e_));" when var5, var6 wanted writefln(assertEqual9(var5,var6)); // not allowed at CT :( mixin(assertEqual9(var5,var6)) } UnitTest.d(246): Error: variable var5 is used before initialization UnitTest.d(246): Error: cannot evaluate assertEqual8(var5,var6) at compile time UnitTest.d(246): Error: argument to mixin must be a string, not (assertEqual8(va r5,var6)) Bruce Adams Wrote:Right, thanks. I'm nearly there. Here's where I'm at: // for example only void assertWrapper(char[] file_, uint line_) { _d_assert(file_,line_); } // the run-time assert part void assertEqual(Type)(Type actual_, Type expected_, char[] file_, uint line_) { if (actual_ != expected_) { writefln("Equality assertion failed"); writefln("actual: '", actual_, "'"); writefln("expected: '", expected_, "'"); } assertWrapper(file_,line_); } const char[] assertEqual4(const char[] actualVar_, const char[] expectedVar_) { return "assertEqual!(int)("[] ~actualVar_ ~","[] ~expectedVar_ ~",cast(char[])(__FILE__),__LINE__);"[]; } const char[] assertEqual5(T)(const char[] actualVar_, const char[] expectedVar_) { return "assertEqual!(" ~typeof(T).stringof ~")("[] ~actualVar_ ~","[] ~expectedVar_ ~",cast(char[])(__FILE__),__LINE__);"[]; } unittest { // declare at run-time const int var3 = 1; const int var4 = 2; int var5 = 1; int var6 = 2; // okay - but must give name of variables writefln(assertEqual4("var5"[],"var6"[])); mixin(assertEqual4("var5"[],"var6"[])); // must provide name and type mixin(assertEqual5!(int)("var5"[],"var6"[])); // must provide name and type mixin(assertEqual5!(typeof(var5))(var5.stringof,var6.stringof)); } I reckon "mixin(assertEqual(var5,var6));" should be possible but I haven't quite figured it out yet. Regards, Bruce. Lutger Wrote: [Some helpful stuff]
Aug 21 2007
Bruce Adams wrote:Hi all, Still stuck. How do I get the template to include the name of a run-time variable at compile-time? Please somebody put me out of my misery. template assertEqual8(actual_, expected_) { const char[] assertEqual8(actual_ a_, expected_ e_) { return "mixin(assertEqual5!("[] ~actual_.stringof ~")("[] ~a_.stringof ~","[] ~e_.stringof ~"));"[]; } } alias assertEqual8!(int, int) assertEqual9; unittest { // all fine // writes: "mixin(assertEqual5!(int)(a_,e_));" when var5, var6 wanted writefln(assertEqual9(var5,var6)); // not allowed at CT :( mixin(assertEqual9(var5,var6)) } UnitTest.d(246): Error: variable var5 is used before initialization UnitTest.d(246): Error: cannot evaluate assertEqual8(var5,var6) at compile time UnitTest.d(246): Error: argument to mixin must be a string, not (assertEqual8(va r5,var6)) Bruce Adams Wrote:Try this... void assertEqual (T) (T actual, T expected, char[] file, uint line) { if (actual != expected) { writeln("Equality assertion failed."); writefln(" actual: '%s'", actual); writefln(" expected: '%s'", expected); _d_assert(file, line); } } template MAssertEqual (alias actual, alias expected) { const MAssertEqual = `assertEqual(` ~actual.stringof~ `, ` ~expected.stringof~ `, __FILE__, __LINE__);` ; } unittest { int var5 = 1; int var6 = 2; mixin(MAssertEqual!(var5, var6)); // -> assertEqual(var5, var6, __FILE__, __LINE__); } Note: Untested. -- Chris Nicholson-SaulsRight, thanks. I'm nearly there. Here's where I'm at: // for example only void assertWrapper(char[] file_, uint line_) { _d_assert(file_,line_); } // the run-time assert part void assertEqual(Type)(Type actual_, Type expected_, char[] file_, uint line_) { if (actual_ != expected_) { writefln("Equality assertion failed"); writefln("actual: '", actual_, "'"); writefln("expected: '", expected_, "'"); } assertWrapper(file_,line_); } const char[] assertEqual4(const char[] actualVar_, const char[] expectedVar_) { return "assertEqual!(int)("[] ~actualVar_ ~","[] ~expectedVar_ ~",cast(char[])(__FILE__),__LINE__);"[]; } const char[] assertEqual5(T)(const char[] actualVar_, const char[] expectedVar_) { return "assertEqual!(" ~typeof(T).stringof ~")("[] ~actualVar_ ~","[] ~expectedVar_ ~",cast(char[])(__FILE__),__LINE__);"[]; } unittest { // declare at run-time const int var3 = 1; const int var4 = 2; int var5 = 1; int var6 = 2; // okay - but must give name of variables writefln(assertEqual4("var5"[],"var6"[])); mixin(assertEqual4("var5"[],"var6"[])); // must provide name and type mixin(assertEqual5!(int)("var5"[],"var6"[])); // must provide name and type mixin(assertEqual5!(typeof(var5))(var5.stringof,var6.stringof)); } I reckon "mixin(assertEqual(var5,var6));" should be possible but I haven't quite figured it out yet. Regards, Bruce. Lutger Wrote: [Some helpful stuff]
Aug 22 2007
Chris Nicholson-Sauls Wrote:Try this... void assertEqual (T) (T actual, T expected, char[] file, uint line) { if (actual != expected) { writeln("Equality assertion failed."); writefln(" actual: '%s'", actual); writefln(" expected: '%s'", expected); _d_assert(file, line); } } template MAssertEqual (alias actual, alias expected) { const MAssertEqual = `assertEqual(` ~actual.stringof~ `, ` ~expected.stringof~ `, __FILE__, __LINE__);` ; } unittest { int var5 = 1; int var6 = 2; mixin(MAssertEqual!(var5, var6)); // -> assertEqual(var5, var6, __FILE__, __LINE__); } Note: Untested. -- Chris Nicholson-SaulsThat doesn't work, but this does: template MAssertEqual (alias actual, alias expected) { const AssertEqual = `assertEqual!(` ~typeof(actual).stringof ~`)(` ~actual.stringof~ `, ` ~expected.stringof~ `, cast(char[])(__FILE__), __LINE__);` ; } So problem solved. I'm not sure why template type deduction isn't working here (a D2.0 issue?), but thanks anyway. I can now run away and write Dunit if someone hasn't already done it. Of course, if they had and I knew about it I probably wouldn't have started this thread. Thanks, I've learned a bit more useful template meta foo. Regards, Bruce
Aug 24 2007
Okay. So having realised my first idea is not possible with current D (prove me wrong please!). What about the char array versus string malarcky. Can anyone help with that? How do I get "foo" to be implicitly interpreted as char[] rather than char[3]. I though the WYSIWYG thing might do it i.e. r"foo" but it doesn't. In fact I'm not sure what it does do. Bruce Adams Wrote:Hi, Another FAQ item I couldn't find. I'm looking to implement some wrappers around assert as assert is too primative and doesn't give me enough information. I want to go down the xUnit path E.g. in the code: assertEqual(foo,bar); leading to output: Equality assertion failed in file.cpp at line 10 Expected Value: 10 Actual Value: 5 I got as far as: void assertEqual(Type)(Type actual_, Type expected_, char[] file_, uint line_) { if (actual_ != expected_) { writefln("Equality assertion failed"); writefln("actual: '", actual_, "'"); writefln("expected: '", expected_, "'"); _d_assert(file_,line_); } } This lets me use: assertEqual!(string)("foo","bar",cast(char[])(__FILE__),__LINE__); Where I'm having trouble is getting __FILE__ and __LINE__ added (presumably using a mixin) so I can avoid the cruft. As a side issue how do I get string constants implicitly converted to char[] or better yet string. In my example above assertEqual("foo","bar",cast(char[])(__FILE__),__LINE__); also works but only because "foo".length == "bar".length // broken! assertEqual("foo","bar2",cast(char[])(__FILE__),__LINE__); Final question to prove I'm a noob. In std.asserterror shouldn't _d_assert(char[] file_,uint line_); actually be: _d_assert(string file_,uint line_); Regards, Bruce.
Aug 16 2007
Bruce Adams schrieb:How do I get "foo" to be implicitly interpreted as char[] rather than char[3]."foo"[]
Aug 16 2007
Frank Benoit Wrote:Bruce Adams schrieb:Thanks. Where is that piece of syntactic sugar documented? There's still a problem though. The following line: assertEqual("foo"[],"bar2"[],cast(char[])(__FILE__),__LINE__); fails to compile with: UnitTest.d(49): template UnitTest.assertEqual(Type) cannot deduce template fun ion from argument types (invariant(char)[],invariant(char)[],char[],long) but the explicit instantiation below works. assertEqual!(invariant(char[]))("foo"[],"bar2"[],cast(char[])(__FILE__),__LINE__); What's going on? Regards, Bruce.How do I get "foo" to be implicitly interpreted as char[] rather than char[3]."foo"[]
Aug 16 2007
Bruce Adams wrote:Frank Benoit Wrote:http://www.digitalmars.com/d/arrays.html "The [] is shorthand for a slice of the entire array." -- Remove ".doesnotlike.spam" from the mail address.Bruce Adams schrieb:Thanks. Where is that piece of syntactic sugar documented?How do I get "foo" to be implicitly interpreted as char[] rather than char[3]."foo"[]
Aug 17 2007