www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A thought for template alias parameters?

reply Trip Volpe <mraccident gmail.com> writes:
I'm working on a set of handy unit testing accessories for my projects. What
I'd love to achieve is something like this:

   expectEqual( myFoo,  3 );

Which, on failure, would result in something along the lines of this diagnostic:

   somefile.d:37: Test failure!
        Expected: myFoo == 3
        Actual: 100, 3

Using a bit of template magic with alias parameters, I've come close:

void expectEquals(alias A, alias B)
        ( string file = __FILE__, int line = __LINE__ )
{
	if(A != B) {
		writefln( "Error: %s, line %d", file, line );
		writefln( "    expected: %s == % s",  A.stringof, B.stringof );
		writefln( "    actual: %s, %s", A, B );
	}
}

void main() {
        int myFoo = 100;
        expectEquals! ( myFoo, 3 );
}

This will produce the output

Error: test.d, line 33
    expected: myFoo == 3
    actual: 100, 3


Unfortunately, this isn't quite there yet. Alias parameters can only match
single symbols, so something like this won't compile:

        int myFoo = 100;
        int myArray[5];
        expectEquals! ( myFoo, myArray[0] );

So my question is, would it be a good idea to add the ability in future
versions for template parameters to alias entire expressions? It would be
perfect for making templated assertions like this and probably has many other
metaprogramming applications. As a recent convert from C++, I still miss the
ability of macro systems (like GoogleTest) to print highly reflective unit test
assertion messages.

Would there be any drawbacks or difficulties in doing this that I'm completely
missing?  :-P
Feb 03 2010
next sibling parent reply Joel Anderson <ask me.com> writes:
On 2/3/2010 9:34 PM, Trip Volpe wrote:
 I'm working on a set of handy unit testing accessories for my projects. What
I'd love to achieve is something like this:

     expectEqual( myFoo,  3 );

 Which, on failure, would result in something along the lines of this
diagnostic:

     somefile.d:37: Test failure!
          Expected: myFoo == 3
          Actual: 100, 3

 Using a bit of template magic with alias parameters, I've come close:

 void expectEquals(alias A, alias B)
          ( string file = __FILE__, int line = __LINE__ )
 {
 	if(A != B) {
 		writefln( "Error: %s, line %d", file, line );
 		writefln( "    expected: %s == % s",  A.stringof, B.stringof );
 		writefln( "    actual: %s, %s", A, B );
 	}
 }

 void main() {
          int myFoo = 100;
          expectEquals! ( myFoo, 3 );
 }

 This will produce the output

 Error: test.d, line 33
      expected: myFoo == 3
      actual: 100, 3


 Unfortunately, this isn't quite there yet. Alias parameters can only match
single symbols, so something like this won't compile:

          int myFoo = 100;
          int myArray[5];
          expectEquals! ( myFoo, myArray[0] );

 So my question is, would it be a good idea to add the ability in future
versions for template parameters to alias entire expressions? It would be
perfect for making templated assertions like this and probably has many other
metaprogramming applications. As a recent convert from C++, I still miss the
ability of macro systems (like GoogleTest) to print highly reflective unit test
assertion messages.

 Would there be any drawbacks or difficulties in doing this that I'm completely
missing?  :-P
You could potentially use a mixin to do this. The resulting code would look something like this. void main() { int myFoo = 100; mixin(expectEquals! ( "myFoo == 3" )); }
Feb 03 2010
parent reply Trip Volpe <mraccident gmail.com> writes:
Joel Anderson Wrote:
 
 You could potentially use a mixin to do this.  The resulting code would 
 look something like this.
 
 void main()
 {
            int myFoo = 100;
            mixin(expectEquals! ( "myFoo == 3" ));
 }
Yeah, mixins could work, but they're ugly. ;-) Forcing the user (in this case the unit test writer) to apply extra boilerplate on their own every time they want to make an assertion is really not much of a solution. There's no semantic reason an assertion should involve a mixin, so allowing that detail to bleed into client code is shaky design. I know I'd get tired of wrapping everything in mixin(...) all the time, and in the end it would probably result in me writing fewer unit tests. :-P
Feb 03 2010
parent reply Joel Anderson <ask me.com> writes:
On 2/3/2010 10:27 PM, Trip Volpe wrote:
 Joel Anderson Wrote:
 You could potentially use a mixin to do this.  The resulting code would
 look something like this.

 void main()
 {
             int myFoo = 100;
             mixin(expectEquals! ( "myFoo == 3" ));
 }
Yeah, mixins could work, but they're ugly. ;-) Forcing the user (in this case the unit test writer) to apply extra boilerplate on their own every time they want to make an assertion is really not much of a solution. There's no semantic reason an assertion should involve a mixin, so allowing that detail to bleed into client code is shaky design. I know I'd get tired of wrapping everything in mixin(...) all the time, and in the end it would probably result in me writing fewer unit tests. :-P
That's one of the reasons I've wished D had a nicer syntax for the string mixin format. This one kinda scares people away :p
Feb 03 2010
parent reply Trip Volpe <mraccident gmail.com> writes:
Joel Anderson Wrote:
 
 That's one of the reasons I've wished D had a nicer syntax for the 
 string mixin format.  This one kinda scares people away :p
What kind of syntax do you have in mind? Making mixins less obtrusive might ease aggravation when they're being used as boilerplate, but making it harder to see when they're being used might have downsides as well. :-P For a case like this, though, I still think the best solution would be to allow for template parameters that can alias whole expressions. Together with the other compile-time reflection abilities of D, I think it would make quite a powerful tool.
Feb 04 2010
parent reply Joel Anderson <ask me.com> writes:
On 2/4/2010 4:41 PM, Trip Volpe wrote:
 Joel Anderson Wrote:
 That's one of the reasons I've wished D had a nicer syntax for the
 string mixin format.  This one kinda scares people away :p
What kind of syntax do you have in mind? Making mixins less obtrusive might ease aggravation when they're being used as boilerplate, but making it harder to see when they're being used might have downsides as well. :-P
Not sure. I'm sure there's a syntax that could meet both goals. Something like. void expectsEquals(string data)() { mixin(data); } ... expectsEquals!("myFoo == 3")(); or expectsEquals!("myFoo == 3");
 For a case like this, though, I still think the best solution would be to
allow for template parameters that can alias whole expressions. Together with
the other compile-time reflection abilities of D, I think it would make quite a
powerful tool.
Feb 05 2010
next sibling parent Joel Anderson <ask me.com> writes:
On 2/5/2010 8:55 PM, Joel Anderson wrote:
 On 2/4/2010 4:41 PM, Trip Volpe wrote:
 Joel Anderson Wrote:
 That's one of the reasons I've wished D had a nicer syntax for the
 string mixin format. This one kinda scares people away :p
What kind of syntax do you have in mind? Making mixins less obtrusive might ease aggravation when they're being used as boilerplate, but making it harder to see when they're being used might have downsides as well. :-P
Not sure. I'm sure there's a syntax that could meet both goals. Something like. void expectsEquals(string data)() { mixin(data); }
I mean: void expectsEquals(string data)() { mixin(dostuff(data)); } ... expectsEquals!("myFoo == 3")(); or expectsEquals!("myFoo == 3");
 For a case like this, though, I still think the best solution would be
 to allow for template parameters that can alias whole expressions.
 Together with the other compile-time reflection abilities of D, I
 think it would make quite a powerful tool.
Feb 05 2010
prev sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
On 02/06/2010 05:55 AM, Joel Anderson wrote:
 On 2/4/2010 4:41 PM, Trip Volpe wrote:
 Joel Anderson Wrote:
 That's one of the reasons I've wished D had a nicer syntax for the
 string mixin format. This one kinda scares people away :p
What kind of syntax do you have in mind? Making mixins less obtrusive might ease aggravation when they're being used as boilerplate, but making it harder to see when they're being used might have downsides as well. :-P
Not sure. I'm sure there's a syntax that could meet both goals. Something like. void expectsEquals(string data)() { mixin(data); } ... expectsEquals!("myFoo == 3")(); or expectsEquals!("myFoo == 3");
This syntax should be possible, which also gives syntax highlighting: expectEquals!q{ myFoo == 3 }; But there is the problem that expectEquals is defined in a module which doesn't have access to myFoo. I don't know how to solve that.
Feb 06 2010
prev sibling parent Trip Volpe <mraccident gmail.com> writes:
Lutger Wrote:
 
 This syntax should be possible, which also gives syntax highlighting:
 
 expectEquals!q{ myFoo == 3 };
 
 But there is the problem that expectEquals is defined in a module which 
 doesn't have access to myFoo. I don't know how to solve that.
 
Template alias parameters handle it nicely right now, at least in the case where you only need to use a single symbol and not a compound expression: In mycontracts.d: void expectEqual(alias A, alias B) ( string file = __FILE__, int line = __LINE__ ) { if(A != B) { string msg = format("expected: %s==%s; actual: %s, %s", A.stringof, B.stringof, A, B ); fail( msg, file, line ); } } In somethingelse.d: expectEqual! ( myFoo, 3 ); Even where expectEqual is defined in a different module, the symbol myFoo is still evaluated in the correct context (the context in which expectEqual is being instantiated). What I'm suggesting is that alias parameters be expanded to allow compound expressions. That would allow you to also use object fields, array elements, and so forth in expectEqual, like so: expectEqual! ( foo.baz[2], 42 ); expectEqual! ( wibble(), "OK" ); You could also do expect with a single expression and no awkward boilerplate: expect! ( myFoo == 3 ); I think this would be a great enhancement; I was just curious if there's any good reason _not_ to do this that I'm not aware of. :-)
Feb 14 2010