www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Generate toString() method at compile time

reply Ary Borenszweig <ary esperanto.org.ar> writes:
Hi,

I'd like to generate a toString() method for my classes. For example:

---
import std.stdio;

class Foo {
   int x;
   int y;

   // mixin ...?
}

void main() {
   auto foo = new Foo;
   foo.x = 1;
   foo.y = 2;
   writeln(foo); // prints "Foo(x = 1, y = 2)"
}
---

I tried using getAllMembers, but that also gives me all methods of Foo. 
I just want the fields. I also tried FieldTypeTuple 
(http://dlang.org/phobos/std_traits.html#FieldTypeTuple) but I get that 
gives me the types. And then I can't find anything else in std.traits 
that could help me.

Is this possible?

Thanks,
Ary
Mar 26 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Ary Borenszweig:

 I tried using getAllMembers, but that also gives me all methods 
 of Foo. I just want the fields.
You can try to filter the results of getAllMembers. Bye, bearophile
Mar 26 2014
prev sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 26 March 2014 at 14:16:08 UTC, Ary Borenszweig 
wrote:
 I'd like to generate a toString() method for my classes. For 
 example:
Try this: override string toString() { import std.conv; string s; s ~= this.classinfo.name ~ "("; foreach(idx, val; this.tupleof) { if(idx) s ~= ", "; s ~= this.tupleof[idx].stringof[5 .. $]; s ~= " = "; s ~= to!string(val); } s ~= ")"; return s; } add that method to Foo. $ ./test58 test58.Foo(x = 1, y = 2)
Mar 26 2014
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 3/26/14, 11:47 AM, Adam D. Ruppe wrote:
 On Wednesday, 26 March 2014 at 14:16:08 UTC, Ary Borenszweig wrote:
 I'd like to generate a toString() method for my classes. For example:
Try this: override string toString() { import std.conv; string s; s ~= this.classinfo.name ~ "("; foreach(idx, val; this.tupleof) { if(idx) s ~= ", "; s ~= this.tupleof[idx].stringof[5 .. $]; s ~= " = "; s ~= to!string(val); } s ~= ")"; return s; } add that method to Foo. $ ./test58 test58.Foo(x = 1, y = 2)
Thanks! That's pretty neat. It seems I was missing "tupleof".
Mar 26 2014
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 26 March 2014 at 14:53:55 UTC, Ary Borenszweig 
wrote:
 Thanks! That's pretty neat. It seems I was missing "tupleof".
Yea, there's other options too like __traits(derivedMembers) if(!is(__traits(getMember...)) == function) and stuff like that, filtering like bearophile said, I just think tupleof is the easiest option for this.
Mar 26 2014
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 3/26/14, 11:47 AM, Adam D. Ruppe wrote:
 On Wednesday, 26 March 2014 at 14:16:08 UTC, Ary Borenszweig wrote:
 I'd like to generate a toString() method for my classes. For example:
Try this: override string toString() { import std.conv; string s; s ~= this.classinfo.name ~ "("; foreach(idx, val; this.tupleof) { if(idx) s ~= ", "; s ~= this.tupleof[idx].stringof[5 .. $]; s ~= " = "; s ~= to!string(val); } s ~= ")"; return s; } add that method to Foo. $ ./test58 test58.Foo(x = 1, y = 2)
A small question: tupleof seems to return a tuple of values. So this.tupleof[idx] would return a value. However when you do this.tupleof[idx].stringof that somehow gets the name of the field. Shouldn't it return the name of the value? I'm confused.
Mar 26 2014
next sibling parent "ketmar" <nobodyherethismailsucks gmail.com> writes:
that's what i did in a hurry. it seems to work, but the code is 
very ugly.

[/code]
import std.conv, std.stdio, std.string, std.traits;


string gen_printer (alias cn) () {
   string res = "override string toString () {\nstring res = `" ~ 
cn.stringof ~ "(`;\n";
   bool need_comma = false;
   foreach (i, m; __traits(derivedMembers, cn)) {
     static if (!isCallable!(__traits(getMember, cn, m)) /*&& m != 
"Monitor"*/) {
       if (need_comma) res ~= `res ~= ", ";`; else need_comma = 
true;
       res ~= "res~=`" ~ m ~ " = `~to!string(" ~ m ~ ");";
     }
   }
   res ~= "res~=`)`;\n";
   res ~= "return res;\n}\n";
   return res;
}


class Foo {
   int x;
   int y;

   void test () {}

   mixin(gen_printer!Foo);
}


void main () {
   auto foo = new Foo;
   foo.x = 1;
   foo.y = 2;
   writefln("%s", foo); // prints "Foo(x = 1, y = 2)"
}
[/code]
Mar 26 2014
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 26 March 2014 at 15:01:35 UTC, Ary Borenszweig 
wrote:
 A small question: tupleof seems to return a tuple of values.
Most accurately, it returns a tuple of variables (it is a bit magical). So struct A { int a; string b; } the tupleof there is int a; string b - variables with different types and different names. The stringof works as if you said A.a.stringof - it fetches the name of the variable. The slice I did on it cuts off the "this." from the full name, leaving just the name. Value to string is done with the to!string function, .stringof always works at compile time.
Mar 26 2014