On Sat, Oct 14, 2023 at 3:55 PM Hadriel Kaplan <hkaplan@juniper.net> wrote:
> From: Barry Revzin <barry.revzin@gmail.com>

> I think this approach is kind of a non-starter. We can't have f"x={x}" just mean std::format("x={}", x) for an important reason.

> This means that std::print(f"x={x}") doesn't and can't work - which is the sort of thing that seems important to support - having to write std::print("{}", f"x={x}") is... less than ideal.

> More generally, the issue is that there are a lot of uses of the format API that are not literally std::format(). In the standard we have std::print(), std::format_to(), etc. It would be really nice if those worked with f-strings as well.

I explicitly covered that topic in section 5 of the draft...

We only need to add another overload to std::print(), std::println(), and format_to() - one that accepts a std::string_view or std::string - to make these work:

    std::print(F"hello C++{20 + 6}");
    std::println(F"hello C++{13 * 2}");
    std::format_to(std::back_inserter(buffer), F"hello C++{26}");

You can't do that though, since you can already call std::print, etc., with just a string literal - and the meaning of std::print with just a format string is distinct from the meaning of what you're suggesting calling std::print with a std::string would be. For instance, std::print("X{{}}") is a valid call today - which prints "X{}" - because we always interpret the first argument as a format string. If we add this overload, it would suddenly print "X{{}}", because we would interpret the argument as just a string. 

Incidentally, Rust tried to do this for a while where panic!("{}", 1) would panic with the message "1" but panic!("{}") would not be interpreted as a format string due to the lack of arguments - the latter would have panicked with the message "{}". But Rust 2021 fixed that by making panic!("{}") ill-formed (since it's a format string expecting one argument, which isn't provided). I'm not saying we should avoid making this change from one to the other simply because Rust just made the change in the opposite direction, but it is a data point.
 

The first two functions might not be as efficient as using a separate format_string and args, because an intermediate std::string is created which might be avoidable otherwise.

But they're already writing to stdout, so an extra temporary string seems rather meaningless? At least I think it's reasonable tradeoff for convenience.

This isn't a good argument though, because while std::print does write to stdout, not all uses of the format API do. format_to() doesn't. Not all uses of the format API are even formatting synchronously - some simply do type-checking in the front end and serialize their arguments to be formatted later. For such uses, an extra temporary string is a complete non-starter.
 

Likewise, this should work without spdlog being changed at all:

    spdlog::info(f"x={x}");

...because *anything* that accepts a std::string rvalue should work - because ultimately the f-string resolves to the std::string returned by std::format().

TIL apparently spdlog makes the mistake that I describe above, where spdlog::info("{}") actually works and logs "{}" (instead of being ill-formed) while spdlog::info("{}", 1) logs "1". I consider that a design mistake, and definitely not one the standard library should adopt. 

It's not even documented behavior, spdlog just says it uses fmt. But fmt::print("{}") isn't valid. 
 

You can almost ignore the fact it uses std::format(), and instead just think of an f-string as a std::string&&. Or perhaps like a new std::to_string(fmt) function. The fact it uses std::format() to create the string is arguably an implementation detail, in some sense.

---

As an aside: it's actually odd to me that std::print/println do NOT already accept a single std::string_view arg today. They do if the string/string_view is constexpr (via implicit conversion), but not a runtime one. I mean I know why it's happening, but it seems wrong to me - an unexpected surprise as a user, and an unnecessary restriction for those functions.

---

Thanks for the review!

-hadriel



Juniper Public