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 15:45:13 +0100
Well,we could very easily get into trouble with just this simple scenario:
user has defined type foo_t. user
specialise formatter_uses_ostream_insertion<foo_t> = true;
foo_t is also a range and it fullfills all requirements for the
formatter<range> specialisation. Now we get an ambiguous specialisation.
Having an extra check for `formatter_uses_ostream_insertion` does not scale
very well.

// Robin

On Fri, Dec 13, 2024 at 3:38 PM Jonathan Wakely <cxx_at_[hidden]> wrote:

>
>
> On Fri, 13 Dec 2024 at 13:55, Robin Savonen Söderholm via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> The specialisation of ` formatter_uses_ostream_insertion ` would be
>> explicit itself, but having:
>> ```cpp
>> template <typename T>
>> requires(formatter_uses_ostream_insertion<T>)
>> struct formatter {...};
>> ```
>>
>
> Ah I see what you mean. Not ambiguities in the variable template, but
> between the formatter partial specialization above and _some other formater
> specialization_ coming from somewhere else (possibly a future version of
> the standard library).
>
> Yes, that could be a problem. Although for another specialization coming
> from the standard library, it would be up to the standard library to ensure
> the specializations are disjoint (either by using subsumption or explicitly
> checking both variables). For user specializations, it would be user error
> to specialize the variable template *and* provide your own specialization
> (although most of the time the latter would be more specialized and so
> would be unambiguous).
>
>
>
>> is not. I worry slightly that the formatter partial specialisation that
>> relies on ` formatter_uses_ostream_insertion` could spell trouble, more
>> specifically if we would be adding more variable-dependent specialisations
>> of a formatter. But maybe you propose that the variable is used in a
>> different way that I did not think of? Like when trying to instantiate the
>> regular formatter and failing, one could test that variable as well and
>> return a 'generic formatter that delegates to ostream'?
>>
>> // Robin
>>
>> On Fri, Dec 13, 2024 at 2:28 PM Jonathan Wakely <cxx_at_[hidden]> wrote:
>>
>>>
>>>
>>> On Fri, 13 Dec 2024 at 13:14, Robin Savonen Söderholm via Std-Proposals <
>>> std-proposals_at_[hidden]> wrote:
>>>
>>>> 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
>>>>
>>>
>>> What's magic about it?
>>>
>>>
>>>> (which I'd refrain from just for the sake of potential
>>>> ambiguous/conflicting specialisations),
>>>>
>>>
>>> Why is that any more likely for a variable template than for a class
>>> template?
>>>
>>>
>>>> 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.
>>>>
>>>
>>> Specializing a variable template is also explicit, and it's how we
>>> opt-in or opt-out of various C++20 features, see enable_view,
>>> enable_borrowed_range, disabled_sized_range. And it's also used in <format>
>>> already, see enable_nonlocking_formatter_optimization and foramt_kind.
>>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>

Received on 2024-12-13 14:45:42