On 01/13/2016 11:41 PM, Jacob Carlborg wrote:

> what if I need to format a third party type that I cannot add
> methods to? UFCS does not seem to work.

Here is an experiment that wraps the third party type to provide a lazy toString:

import std.stdio;
import std.format;
import std.array;
import std.algorithm;
import std.range;

/* Wraps an element and provides a lazy toString that dispatches the work to a
 * user-provided 'formatter' function. E is the element type. */
struct Formatted(alias formatter, E) {
    E element;

    void toString(void delegate(const(char)[]) sink) const {
        formatter(sink, element);
    }
}

/* Adapts a range by converting the elements to 'Formatted'. R is the range
 * type. */
auto formatted(alias formatter, R)(R range) {
    return range.map!(e => Formatted!(formatter, ElementType!R)(e));
}

/* A third party test type that does not have a lazy toString member
 * function. */
struct Foo {
    double d;
    string s;
}

void main() {
        auto data = [ Foo(1.5, "hello"), Foo(2.5, "world") ];

    auto buf = appender!string();
        formattedWrite(buf, "%(%s\n%)",
                   data.formatted!(
                       (sink, a) => formattedWrite(sink, "%s and %s",
                                                   a.d, a.s)));

    writeln(buf.data);
}

Prints the objects according to the user's lambda:

1.5 and hello
2.5 and world

It would be great if the user could provide just the format and accessed the members:

    data.formatted!("%s and %s", a.d, a.s); // <-- ERROR

But I couldn't get it working because the compiler does not know what 'a' is at that point. It might be acceptable to provide a lambda per member but then it gets to cluttered:

    data.formatted!("%s and %s", a => a.d, a => a.s); // Might work

Ali

Reply via email to