C++ Logo

std-proposals

Advanced search

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

From: Victor Zverovich <victor.zverovich_at_[hidden]>
Date: Sat, 14 Dec 2024 07:32:55 -0800
Note that std::print already supports printing to an ostream so you don't
need lazy_format or print_to_sink:
https://en.cppreference.com/w/cpp/io/basic_ostream/print

Cheers,
Victor

On Sat, Dec 14, 2024 at 4:22 AM Robin Savonen Söderholm via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> How is `print_to_sink` different from
> `format_to(ostreambuf_iterator<char>(cout), <format string>,
> ...<arguments>)`? More than verbosity of course..
>
> I hacked together some implementations here: (godbolt):
> https://godbolt.org/z/G44dEabjq and (github): doocman/dtl: Doocman
> Template Library (C++) <https://github.com/doocman/dtl> .
> There are probably details about these that could be improved, but my hope
> is that the overall behaviour is fine, but I'll gladly take some feedback
> while trying to write out a proper proposal.
>
> // Robin
>
> On Fri, Dec 13, 2024 at 10:05 PM Tiago Freire <tmiguelf_at_[hidden]>
> wrote:
>
>> The lazy_format, although not entirely a bad idea what you gain from it
>> is mostly peanuts.
>>
>> Because of the way the operator << mechanism works the implementation has
>> to resolve each of the segments (i.e. << “segment one” << “segment two” <<
>> etc..) one by one and cannot optimize with the fact that there are more
>> segments following, this despite the fact that all segments will be
>> available in the stack before the last “<<” in the chain completes.
>>
>> A much more efficient way to handle this is to completely forgo the usage
>> of any ostream operator and just pass the ostream object as one of the
>> arguments, like so:
>>
>> print_to_sink(std::cout, "formatting string", …arguments); //pass
>> arguments as a reference is better.
>>
>>
>>
>> Which can be made more generic as:
>>
>> print_to_sink<underlying_encoding>(<generic_sink>, <format string>,
>> …<arguments>);
>>
>>
>>
>> This allows for the function to introspect about all arguments and be
>> much more efficient (and I’m talking easily a couple of orders of magnitude
>> faster, not just a mere 10%).
>>
>>
>>
>> Take it for what it is, it is an opinion, my opinion is that std::ostream
>> is obsolete and should eventually be deprecated and removed, it’s a waste
>> of time to try and upgrade it.
>>
>>
>>
>>
>>
>>
>>
>> *From:* Std-Proposals <std-proposals-bounces_at_[hidden]> *On
>> Behalf Of *Robin Savonen Söderholm via Std-Proposals
>> *Sent:* Friday, December 13, 2024 2:14 PM
>> *To:* std-proposals_at_[hidden]
>> *Cc:* Robin Savonen Söderholm <robinsavonensoderholm_at_[hidden]>
>> *Subject:* Re: [std-proposals] Format output-streamable types?
>>
>>
>>
>> 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.
>>
>>
>>
>>
>>
>> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2024-12-14 15:33:07