C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Proposal to add f-strings to C++ as an improvement of string formatting

From: Barry Revzin <barry.revzin_at_[hidden]>
Date: Wed, 28 Dec 2022 12:00:43 -0500
On Wed, Dec 28, 2022 at 10:12 AM Jason McKesson via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Wed, Dec 28, 2022 at 3:17 AM Henry Miller via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> > Your second objection to format is a basic requirement of all string
> systems, and so any string library, in any programming language that
> doesn't feature that is wrong. It is critical that all programmers get
> used to the idea their strings may need to be translated to some foreign
> language. Different languages feature different word order and so the
> order the variables appear in the output must be flexible.
> >
> > While is seems likely that any string that gets translated will use the
> strings provided by their UI toolkit and not std, we should still encourage
> the mindset in young programmers that variables should not be in the same
> order as the output.
>
> I should point out that this notion also speaks to the intended
> durability of programs written in a certain language.
>
> Python code is probably the most prolific user of interpolated
> strings. But there's a lot of Python code that is written for
> scripting purposes. In these cases, most formatted strings aren't UI
> strings that would ever need translating.
>

One of {fmt}'s main advantages over iostreams is the ability to do
translation well. But not every domain requires translation. I've never
worked in a C++ domain that does - but that doesn't mean my C++ code is
written for scripting purposes or is ephemeral. I just don't need
translation.


>
> But here's the thing. No matter how "script friendly" C++ becomes, I'm
> never going to use C++ for a scripting-style task. Python exists, and
> is way more trivial to use, so I will use it for those tasks. C++ is
> meant for more durable code, stuff that you build and maintain, so the
> need for translation increases.
>

Indeed, which is precisely why interpolated strings are *such* a useful
tool for C++: they are more durable. The main disadvantage with {fmt} as
compared to iostreams is that all your arguments just go at the end - so if
you're formatting 3, 4, 6, 10 arguments, it's just hard to ensure that you
actually put them in the correct order - whereas with iostreams this is
very obvious. Interpolating strings lets you stick in the arguments
in-place, making the code more obviously correct by construction.


>
> Basically, consider this: in Python, interpolated strings are a
> language feature, while formatted strings are a standard library
> feature. That says something about the expectations of Python.
> Interpolated strings are not inherently bad practice, but they are bad
> practices within certain contexts.
>
> Contexts that C++ widely gets used in.
>
> So however nifty they are, they also push C++ programmers to think of
> their code as ephemeral. Which it almost never is.
>

I'm not sure what interpolated strings have anything to do with
ephemerality. Rust has interpolated literals too, by the way.

Anyway, back to the OP. iostreams have no need for f-strings. This (and,
btw, you forgot the f everywhere):

std::cout << f"threshold is {threshold}, time is now {time()}" <<
std::endl;

is shorter than this, by 9 characters:

std::cout << "threshold is " << threshold << ", time is now " << time()
<< std::endl;

But otherwise it's not really that big a difference and it's not really
worth it to support. Sure it's more readable in the sense that there are
fewer <<s, but... meh.

On the other hand this:

fmt::print(f"threshold is {threshold}, time is now {time()}\n");

is substantially better (though not much shorter) than this:

fmt::print("threshold is {}, time is now {}\n", threshold, time());

I think this facility is really only worth supporting for format. Plus, I
think it's exceedingly hard to come up with a way for it to work for both
format and iostreams to begin with, since their respective syntaxes are so
different. So there's more value in supporting just the better one.

Other notes:

   - the expressions in {} need not be rvalues, threshold is an lvalue.
   They're just arbitrary expressions
   - interpolated literals need to handle specifiers, so print(f"value in
   hex is {value:#08x}") needs to evaluate as print("value in hex is {:#08x}",
   value)
   - specifiers themselves can also have expressions. For instance in
Rust println!("Total
   score is [{total_score:>width$}]") is printing the value of total_score with
   a width of width. In Python, this is f"Total score is
   {total_score:>{width}}"
   - brace escaping in both Python and Rust is {{ not \{


Barry

Received on 2022-12-28 17:00:53