Boost logo

Boost :

Subject: Re: [boost] Variadic append for std::string
From: Hans Dembinski (hans.dembinski_at_[hidden])
Date: 2017-01-24 05:52:13


Hi Christof

> Would you prefer str(), or implicit conversion?

Please, no implicit conversion. I don't like .str() but it is better than implicit conversion. Implicit conversion is confusing, especially in the context of templates and auto.

I quote the Zen of Python: "Explicit is better than implicit".

> There is several usecases:
> […]

Ok, you made a point. Also, it follows the principle of least surprise if concat and friends all consistently return string factories.

> Am 24.01.2017 00:14, schrieb Gavin Lambert:
>> On 24/01/2017 07:26, Olaf van der Spek wrote:
>>>> 1. scope for formatting tags:
>>>> concat(format::hex<int>, 42, " is hex for ", concat(42)).str();
>>>> Here the inner concat will convert the 42 to its decimal representation,
>>>> while the outer one converts the first 42 to its hex representation.
>>> Wouldn't concat(hex(42), " is hex for", 42) make more sense?
>> +1. If you like persistent formatting states (and all the unexpected
>> fun they cause when you forget to cancel them), use iostreams instead.
>
> My proposal does not have anything similar to iostream manipulators. The conversion tags always extend to the whole concat parameter list, but not to enclosed calls to concat. There is no change in behavior that depends on the position of the conversion tag in the parameter list. Actually I think, we should be able to put all the conversion tags at the beginning of the parameter list.

+1 for concat(hex(42), " is hex for", 42)

If format::hex<int> is allowed to be in any position and still affect the whole string, that is not at all intuitive. What happens when you have two conflicting formatting tags in the argument list? Which one wins? Hard to judge for the user without looking into the reference manual. A design is intuitive if you don't need to look up stuff in the reference manual all the time. There is the principe of least surprise again.

If you use template magic to detect the conflicting formatting tags and raise a compile time error, you are still left the the natural exception of users that positions in "concat" matter. They matter for all the other arguments, so why shouldn't they matter for the formatting tags. It is breaking an implicit rule. Let's get rid of them to avoid the whole mess.

As other people pointed out, formatting tags should be left to streams, where they make sense. They don't make sense in concat, because the mental model for "concat" is "function call", not "stream". The most intuitive in this case to use explicit converters, unary functions like hex(42). Everyone who reads that can immediately understand what it does and that the call to hex(…) does not affect the other arguments in concat. Of course, hex(42) would return a string factory.

> join(hex<int>, separator(" "), my_nums);
>
> Here all the numbers are converted to their hex representation. With your approach this would look like:
>
> join(separator(" "),
> my_nums | transform([](int i) -> int {
> return hex(i);
> }));
>
> That is much more difficult to understand.

I don't see how that follows. You are free to write an overload for "join" which accepts an unary function as the first argument, which is then applied to all the values in the range. If hex<int> is an unary function, the first version makes even more sense.

The second version I reject regardless, because it uses an overloaded operator |. Operator overloads are ambiguous outside the realm of mathematics. We want to follow the principle of least surprise, and this is surprising syntax.

Hans


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk