Summary: formatValue: range templates introduce 3 bugs related
                    to class & struct cases
           Product: D
           Version: D2
          Platform: x86
        OS/Version: Linux
            Status: NEW
          Severity: blocker
          Priority: P2
         Component: Phobos

--- Comment #0 from Denis Derman <> 2010-12-15 01:36:05 PST 
formatValue: range templates introduce 3 bugs related to class & struct cases

This issue concerns class case, the struct case, and the 3 range cases of the
set of formatValue templates in std.format. As this set is currently written
and commented (1), it seems to be intended to determine the following cases
(about class/struct/range only):

* An input range is formatted like an array.
* A class object is formatted using toString.
* A struct is formatted:
    ~ using an input range interface, if it implements one,
    ~ using toString, if it defines it,
    ~ in last resort, using the type's 'stringof' property.

To be short: I think the right thing to do is to remove range cases.
Explanations, details, & reasoning below.

In the way the set of templates is presently implemented, and because of how
template selection works (as opposed to inheritance, eg), the following 3 bugs
come up:

1. When a class defines an input range, compiler-error due to the fact that
both class and input range cases match:
    /usr/include/d/dmd/phobos/std/format.d(1404): Error: template
std.format.formatValue(Writer,T,Char) if (is(const(T) == const(void[])))
formatValue(Writer,T,Char) if (is(const(T) == const(void[]))) matches more than
one template declaration,
/usr/include/d/dmd/phobos/std/format.d(1187):formatValue(Writer,T,Char) if
(isInputRange!(T) && !isSomeString!(T) && isSomeChar!(ElementType!(T))) and
/usr/include/d/dmd/phobos/std/format.d(1260):formatValue(Writer,T,Char) if
(is(T == class))
This, due to inheritance from Object, even if no toString is _explicitely_

2. For a struct, a programmer-defined output format in toString is shortcut if
ever the struct implements a range interface!

3. If a range's element type (result type of front) is identical to the range's
own type, writing runs into an infinite loop... This is well possible, for
instance a textual type working like strings in high-level/dynamic languages (a
character is a singleton string).

To solve these bugs, I guess the following changes would have to be done:
* The 3 ranges case must have 2 additional _negative_ constraints:
    ~ no toString defined on the type
    ~ (ElementType!T != T)
* The struct case must be split in 2 sub-cases:
    ~ use toString if defined
    ~ [else use range if defined, as given above]
    ~ if neither toString nore range, use T.stringof

I have tried to implement and test this modif, but ran into build errors
(seemingly unrelated, about isTuple) I could not solve.

Now, I think it is worth wondering whether all these complications, only to
have _default_ formatValue's for input ranges, is worth it at all. On one hand,
in view of the analogy, it looks like a nice idea to have them expressed like
arrays. On the other, when can this feature be useful?
An first issue comes up because there is no way, AFAIK, to tell apart inherited
and explicite toString methods of classes: is(typeof(val.toString() == string))
is always true for a class. So that the range case would never be triggered for
classes -- only for structs.
So, to use this feature, (1) the type must be a struct (2) which defines no
toString (3) whch implements a range interface, and (4) the range's element
type must not be the range type itself. In addition, the most sensible output
form for it should be precisely the one of an array.
Note that unlike for structs, programmers cannot define custom forms of array
output ;-) This is the reason why a default array format is so helpful -- but
this reason does not exist for structs, thank to toString (and later writeTo).
If no default form exists for ranges, then in the rare cases where a programmer
would implement a range interface on a struct _and_ need to re-create an
array-like format for it, this takes a few lines in toString, for instance:
    string toString () {
        string[] contents = new string[this.elements.length]; 
        foreach (i,e ; this.elements)
            contents[i] = to!string(this.elements[i]);
        return format("[%s]", join(contents, ", "));

As a conclusion, I would recommend to get rid of the (3) range cases in the set
of formatValue templates. (This would directly restore correctness, I guess
--showing that range cases where probably added later.)

(1) There is at least a doc/comment error, namely for the struct case
(commentted as AA instead). Also, the online doc does not hold template
constraints, so that it is not possible to determine which one is selected in
given situations.


Configure issuemail:
------- You are receiving this mail because: -------

Reply via email to