C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Format output-streamable types?

From: Robin Savonen Söderholm <robinsavonensoderholm_at_[hidden]>
Date: Fri, 13 Dec 2024 14:14:21 +0100
Ok, taking feedback from you both I propose this:

- Add even more specialisations to std::formatter (one paper)

- Look at a way to let users delegate the format (output stream) to the
output stream (formatter).
The first one feels somewhat trivial, and I do think that the standard
should have a formatter specialisation for all types that already provide
an operator << to an ostream.
The second one may need some design, I'll try to work it out during the
weekend. At the top of my head I was thinking about something like this:
To format <<:
```c++
std::print("foo only provides operator<< but here I print it anyway: {}",
stream_formatter(foo));
```
The other one I think could be nicely solved by another construct that I
have been wanting to implement: lazy_formatted_string and lazy_format, so
that we could write something like this:
```c++
std::cout << "bar only supports the modern formatting facilities but here I
let a lazy_formatted_string to efficiently stream to cout without
intermediate allocations " << lazy_format("{}", bar);
```.
My main concern with these API:s are wheter they should capture by value,
by reference or some automatic version of either (value for trivial types
and for r-value inputs, reference for non-trivial lvalue inputs for
example). I also like the idea of the lazy_format to allow some
optimisations in the std::string::operator= department:
```cpp
std::string str = ...;
// and some while later:
// same as str.clear(); std::format_to(std::backinserter(str), "...", ...);
str = lazy_format("....", ...);
```
but these lazy-evaluation stuff may lead to life-time issues for careless
users (although we already have the same issue with std::span and
std::string_view).

Anyway, if no objections are made I'll look into some implementations later
and hopefully come back with a more fleshed-out proposal. Thanks!

PS: rather than having the magic ` formatter_uses_ostream_insertion
`-variable (which I'd refrain from just for the sake of potential
ambiguous/conflicting specialisations), I think it would make more sense to
have a template-base class that you may use for your formatter:

```cpp
namespace std {
template <typename Char>
struct formatter<my_namespace::foo, Char> :
ostream_formatter<my_namespace::foo, Char> {};
}
```
.
I prefer it because it feels slightly more explicit.
DS.

// Robin

On Thu, Dec 12, 2024, 22:21 Jonathan Wakely <cxx_at_[hidden]> wrote:

>
>
> On Thu, 12 Dec 2024 at 20:24, Robin Savonen Söderholm via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> Ok, so just a proposal to add more formatter specialisations?
>>
>
> See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1636r2.pdf
>
> Formatters for thread::id and filesystem::path have now been added, the
> rest haven't.
>
>
>
>> Users wanting "auto conversions" would have to implement them themselves.
>>
>
> I like the idea of some kind of reuse between operator<< and
> std::formatter, maybe opt-in. I see no reason why we would ever want to
> completely remove basic_ostream.
>
> We could have a variable template like:
>
> template<typename T>
> constexpr bool formatter_uses_ostream_insertion = false;
>
> and then define a formatter specialization that only supports empty format
> specs and uses operator<< to produce a string/wstring:
>
> template<typename T>
> requires formatter_uses_ostream_insertion<T>
> class formatter<T> // ...
>
> We could also provide the reverse opt-in, so that ostream insertion uses
> std::format (or std::format_to) to write to the ostream.
>
> And we could provide a generic std::to_string(T) function which uses
> std::format if the type is formattable, and operator<< if that works (and
> is ill-formed otherwise).
>
> There's lots of room for improvement in this space.
>
>
>

Received on 2024-12-13 13:14:50