www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Generating custom toString for structs

reply Renato <renato athaydes.com> writes:
Hi, I wanted to customize the toString implementation for my 
structs.

So I wrote a mixin for doing that:

```
import std.traits : FieldNameTuple;
import std.format : FormatSpec;
import std.conv : to;
import std.range : put;

private mixin template StructToString(S)
{
     void toString(scope void delegate(const(char)[]) sink,
         FormatSpec!char fmt)
     {
         put(sink, typeid(S).toString);
         put(sink, "(\n");
         foreach (index, name; FieldNameTuple!S)
         {
             put(sink, "  ");
             put(sink, name);
             put(sink, ": ");
             put(sink, this.tupleof[index].to!string);
             put(sink, ",\n");
         }
         put(sink, ")");
     }
}
```

I would've expected this sort of thing (various ways of 
implementing toString for structs and classes) to be already 
provided by some library but I couldn't find any.

Anyway, my questions are:

Is the above a "good" way to do this?

Are there libraries (or even something in Phobos?) that already 
provide some mixins or equivalent functionality?

For reference, I wanted something like Java [Lombok's ToString 
annotation](https://projectlombok.org/features/ToString), also 
present in 
[Groovy](https://docs.groovy-lang.org/latest/html/gapi/groovy/transform/ToString.html).
Jan 07
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 7 January 2024 at 09:49:36 UTC, Renato wrote:
 Is the above a "good" way to do this?
It looks ok to me. There are some minor changes I would make, like using `typeof(this)` instead of `S` to refer to the type of the struct you're mixing it into, but the overall approach is fine.
 Are there libraries (or even something in Phobos?) that already 
 provide some mixins or equivalent functionality?

 For reference, I wanted something like Java [Lombok's ToString 
 annotation](https://projectlombok.org/features/ToString), also 
 present in 
 [Groovy](https://docs.groovy-lang.org/latest/html/gapi/groovy/transform/ToString.html).
The [`boilerplate` package][1] has a [`GenerateToString` mixin][2] that looks pretty similar to the examples you linked. [1]: https://code.dlang.org/packages/boilerplate [2]: https://boilerplate.dpldocs.info/v1.9.1/boilerplate.autostring.GenerateToString.html
Jan 07
prev sibling parent reply cc <cc nevernet.com> writes:
On Sunday, 7 January 2024 at 09:49:36 UTC, Renato wrote:
 Hi, I wanted to customize the toString implementation for my 
 structs.

 So I wrote a mixin for doing that:
Alternative format similar to what you already have: ```d import std.format; mixin template ToStringMixin() { void toString(W)(ref W writer) { alias T = typeof(this); writer.formattedWrite("%s(\n", T.stringof); // See also: std.traits.fullyQualifiedName static foreach (idx, field; T.tupleof) { writer.formattedWrite("\t%s: %s\n", T.tupleof[idx].stringof, this.tupleof[idx]); } put(writer, ")"); } } struct Foo { int x, y; string s; mixin ToStringMixin; } void main() { auto foo = Foo(3, 4, "hello"); writeln(foo); } ``` Note however that the templated version of toString(W) can be difficult to debug in some cases as, if it fails to compile, std.format will simply ignore it and not use it while the rest of the program compiles successfully. Any compilation error message can be seen by instead attempting to call the method directly, e.g. something like `auto a = appender!string; foo.toString(a);`. I don't use the delegate version personally, but if that's already working for you, may as well stick with it. If you wanted to avoid including a mixin in all of your structs, another option is to create a generic templated container struct with its own toString: ```d struct ToStringer(T) { T t; // Consider T* t void toString(W)(ref W writer) { //assert(t !is null); writer.formattedWrite("%s(\n", T.stringof); static foreach (idx, field; T.tupleof) { writer.formattedWrite("\t%s: %s\n", T.tupleof[idx].stringof, t.tupleof[idx]); } put(writer, ")"); } } auto ToString(T)(ref T t) { // syntactic sugar return ToStringer!T(t); } struct Foo { int x, y; string s; } void main() { auto foo = Foo(3, 4, "hello"); //writeln(ToStringer!Foo(foo)); writeln(foo.ToString); } ```
Jan 11
parent cc <cc nevernet.com> writes:
On Thursday, 11 January 2024 at 12:45:45 UTC, cc wrote:
 I don't use the delegate version personally, but if that's 
 already working for you, may as well stick with it.
In retrospect, that delegate version is probably quite a bit better.
Jan 11