Thread overview
Two possibilities of fixing format problem, which is better?
Oct 20, 2019
berni44
Oct 20, 2019
rikki cattermole
Oct 21, 2019
berni44
Oct 22, 2019
berni44
Oct 22, 2019
H. S. Teoh
Oct 23, 2019
berni44
Oct 23, 2019
H. S. Teoh
October 20, 2019
This post is ab out issue 9592 [1], namely, that format does ignore the width parameter and the minus flag for some types and for some not (numbers, bool, strings). According to the documentation width is used always and the minus flag only for numbers.

Now I see two ways to fix this:

a) Correct the documentation.
b) Correct the code (in this case I'd change the documentation of the minus flag to include non-number types too).

What do you think is better?


[1] https://issues.dlang.org/show_bug.cgi?id=9592
[2] https://dlang.org/phobos/std_format.html#.formattedWrite
October 21, 2019
On 21/10/2019 6:55 AM, berni44 wrote:
> This post is ab out issue 9592 [1], namely, that format does ignore the width parameter and the minus flag for some types and for some not (numbers, bool, strings). According to the documentation width is used always and the minus flag only for numbers.
> 
> Now I see two ways to fix this:
> 
> a) Correct the documentation.
> b) Correct the code (in this case I'd change the documentation of the minus flag to include non-number types too).
> 
> What do you think is better?
> 
> 
> [1] https://issues.dlang.org/show_bug.cgi?id=9592
> [2] https://dlang.org/phobos/std_format.html#.formattedWrite

What does printf do?

That is your baseline.
October 21, 2019
On Sunday, 20 October 2019 at 23:15:28 UTC, rikki cattermole wrote:
> What does printf do?

b) :-) Thanks.
October 22, 2019
On Monday, 21 October 2019 at 07:18:09 UTC, berni44 wrote:
> On Sunday, 20 October 2019 at 23:15:28 UTC, rikki cattermole wrote:
>> What does printf do?
>
> b) :-) Thanks.

Unfortunately, meanwhile I learned, that there are various implementations of printf with different behaviour. So it doesn't really answer my original question.

Here an example:

import std.stdio;

int[] a = [1,23,-456];
writeln(format("%20s",a));
writeln(format("[%(%20s%|, %)]",a));

produces:

[                   1,                   23,                 -456]
[                   1,                   23,                 -456]

but IMHO the first line should be:

       [1, 23, -456]

This is in accordance with the current Phobos spec and some printf spec I found in the internet. Should this be corrected?

Similar:

import std.stdio;

char[] a = ['a','b','c'];
writeln(format("%20s",a));
writeln(format("[%(%20s%|, %)]",a));

produces:

                 abc
['a', 'b', 'c']

And:

import std.stdio;

String[] a = ["a","b","c"];
writeln(format("%20s",a));
writeln(format("[%(%20s%|, %)]",a));

produces:

["a", "b", "c"]
["a", "b", "c"]

So my question remains: What is considered correct behaviour here?
October 22, 2019
On Tue, Oct 22, 2019 at 04:34:52PM +0000, berni44 via Digitalmars-d wrote: [...]
> import std.stdio;
> 
> int[] a = [1,23,-456];
> writeln(format("%20s",a));
> writeln(format("[%(%20s%|, %)]",a));
> 
> produces:
> 
> [                   1,                   23,                 -456] [                   1,                   23,                 -456]
> 
> but IMHO the first line should be:
> 
>        [1, 23, -456]
> 
> This is in accordance with the current Phobos spec and some printf spec I found in the internet. Should this be corrected?

IMO, yes.  When you write "%20s" you're essentially saying "format the next argument as a string with width 20". It's unexpected behaviour for the width to suddenly apply to elements within the object as opposed to the object itself. If somebody wants the 20 to apply to each element instead, then he should write the second format string as you showed above.


> Similar:
> 
> import std.stdio;
> 
> char[] a = ['a','b','c'];
> writeln(format("%20s",a));
> writeln(format("[%(%20s%|, %)]",a));
> 
> produces:
> 
>                  abc
> ['a', 'b', 'c']
> 
> And:
> 
> import std.stdio;
> 
> String[] a = ["a","b","c"];
> writeln(format("%20s",a));
> writeln(format("[%(%20s%|, %)]",a));
> 
> produces:
> 
> ["a", "b", "c"]
> ["a", "b", "c"]
> 
> So my question remains: What is considered correct behaviour here?

IMO the 20 should apply to whatever object the next argument is *as a whole*, since that's the most useful and expected behaviour. It's unexpected for "%20s" to apply the 20 width to the entire object when the argument is a string, but suddenly changes to 20 width *per element* when the argument is an array that isn't a string.


T

-- 
There are three kinds of people in the world: those who can count, and those who can't.
October 23, 2019
On Tuesday, 22 October 2019 at 17:12:53 UTC, H. S. Teoh wrote:
> IMO, yes.  When you write "%20s" you're essentially saying "format the next argument as a string with width 20". It's unexpected behaviour for the width to suddenly apply to elements within the object as opposed to the object itself.

While trying to implement this I came across a function called formatElement. Next to this there is a comment saying "undocumented because of deprecation". Does anyone know about this "deprecation" of formatElement? It's used inside std.format, e.g. for formating ranges. And it appears in one public example, where it probably should not appear.

The reason I'm asking is, that I'd like to get the following unittest working:

unittest
{
    int[2] a = [-5, 345];
    string t1 = format("[%10s] [%-10s]", a, a);
    assert(t1 == "[ [-5, 345]] [[-5, 345] ]");
}

So far I managed to do this, but now an other unittest pops up, namely:

formatTest( [cast(string)"hello"], `["hello"]` );

This is essentially, because the old implementation called formatElement, while the new one doesn't. Looking inside formatElement, this function formats the element not according to some %... parameter, but similar to how it would be presented as a litteral in source code.

Now I'm unsure, how to continue.
October 23, 2019
On Wed, Oct 23, 2019 at 02:45:51PM +0000, berni44 via Digitalmars-d wrote:
> On Tuesday, 22 October 2019 at 17:12:53 UTC, H. S. Teoh wrote:
> > IMO, yes.  When you write "%20s" you're essentially saying "format the next argument as a string with width 20". It's unexpected behaviour for the width to suddenly apply to elements within the object as opposed to the object itself.
> 
> While trying to implement this I came across a function called formatElement. Next to this there is a comment saying "undocumented because of deprecation". Does anyone know about this "deprecation" of formatElement?

git blame and git log --graph reveal that the "deprecation" came from Phobos PR #2890. Perhaps start by looking there?  What I'd do is to checkout Phobos just before that PR merge and then do a further git blame / git log --graph to find when formatElement was changed before that. Hopefully the history of changes will help you piece together what exactly happened, and perhaps suggest some possible reasons why.


> It's used inside std.format, e.g. for formating ranges. And it appears in one public example, where it probably should not appear.
> 
> The reason I'm asking is, that I'd like to get the following unittest working:
> 
> unittest
> {
>     int[2] a = [-5, 345];
>     string t1 = format("[%10s] [%-10s]", a, a);
>     assert(t1 == "[ [-5, 345]] [[-5, 345] ]");
> }
> 
> So far I managed to do this, but now an other unittest pops up, namely:
> 
> formatTest( [cast(string)"hello"], `["hello"]` );
> 
> This is essentially, because the old implementation called formatElement, while the new one doesn't. Looking inside formatElement, this function formats the element not according to some %... parameter, but similar to how it would be presented as a litteral in source code.
> 
> Now I'm unsure, how to continue.

I don't know the entire story behind this, but what I know is this: in certain cases, std.format was expected to format certain aggregates of strings such that each string was quoted. One case is string[], where the output was expected to be like this:

	["abc", "def", ... ]

rather than:

	[abc, def, ... ]

Why, I'm not really sure, but later on the '-' flag was added to the %s specifier in order to get the second behaviour instead of the first (i.e., "%-s instead of %s").  Obviously, when this was done whoever did it wanted to maintain backward compatibility with the auto-quoting behaviour, so the second behaviour is not default, but you have to explicitly ask for it with the '-' flag.  This is why in certain aggregate formats involving %(...%) I often find myself having to revise it to %-(...%) instead, just so the strings will show up correctly in the output.

I'd suggest you try to maintain backward compatibility with this special case behaviour, if possible, so that existing codebases won't get a nasty surprise (suddenly quoted strings are no longer quoted in the output). AFAIK, this special-casing only applies to string elements; other types should be free of such odd behaviours.

P.S. If it were up to me, I'd try to get rid of this awkward special case, but then I'm not exactly sure how one would go about deprecating a certain aspect of format strings, esp. since most older code use runtime format strings, so you can't get any warning until runtime, which is very bad for deprecations.


T

-- 
That's not a bug; that's a feature!