[PATCH] libstdc++: Provide ability to query _Sink_iter if writes are discarded.
Jonathan Wakely
jwakely@redhat.com
Tue May 6 12:06:43 GMT 2025
On Tue, 6 May 2025 at 12:42, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>
>
>
> On Tue, May 6, 2025 at 1:34 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>>
>> On 05/05/25 14:44 +0200, Tomasz Kamiński wrote:
>> >This patch provides an equality operator between _Sink_iter and default_sentinel,
>> >that returns true, if any further writes to the _Sink_iter and underlying _Sink,
>> >will be discared, and thus can be omitted. This operator is implemented in terms
>> >new of _M_discarding virtual function in _Sink.
>> >
>> >Currently only the _Padding_sink reports discarding mode of if width of sequence
>> >characters is greater than _M_maxwidth (precision), or underlying _Sink is
>> >discarding characters. The _M_discarding override, is separate function from
>> >_M_ignoring, that remain annotated with [[__gnu__::__always_inline__]].
>> >
>> >Despite having notion of maximum characters to be written (_M_max), _Iter_sink
>> >nevers discard characters, as the total number of characters that would be written
>> >needs to be returned by format_to_n. This is documented in-source by providing an
>> >_Iter_sink::_M_discarding override, that always returns false.
>> >
>> >The function is currently queried only by the _Padding_sinks, that may be stacked
>> >for example a range is formatted, with padding with being specified both for range
>> >itself and it's elements. The state of underlying sink is checked during construction
>> >and after each write (_M_sync_discarding).
>> >
>> >libstdc++-v3/ChangeLog:
>> >
>> > * include/std/format (__Sink_iter<_CharT>::operator==)
>> > (__Sink<_CharT>::_M_discarding, _Iter_sink<_CharT, _OutIter>::_M_discarding)
>> > (_Padding_sinl<_CharT, _Out>::_M_padwidth)
>> > (_Padding_sink<_CharT, _Out>::_M_maxwidth): Remove const.
>> > (_Padding_sink<_CharT, _Out>::_M_sync_discarding)
>> > (_Padding_sink<_CharT, _Out>::_M_discarding): Define.
>> > (_Padding_sink<_CharT, _Out>::_Padding_sink(_Out, size_t, size_tt))
>> > (_Padding_sink<_CharT, _Out>::_M_force_update):
>> > (_Padding_sink<_CharT, _Out>::_M_flush): Call _M_sync_discarding.
>> > (_Padding_sink<_CharT, _Out>::_Padding_sink(_Out, size_t)): Delegate.
>> >---
>> > libstdc++-v3/include/std/format | 66 +++++++++++++++++++++++++++------
>> > 1 file changed, 54 insertions(+), 12 deletions(-)
>> >
>> >diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
>> >index 054ce350440..08a28fc92da 100644
>> >--- a/libstdc++-v3/include/std/format
>> >+++ b/libstdc++-v3/include/std/format
>> >@@ -3144,6 +3144,10 @@ namespace __format
>> > auto
>> > _M_reserve(size_t __n) const
>> > { return _M_sink->_M_reserve(__n); }
>> >+
>> >+ friend bool
>> >+ operator==(_Sink_iter __it, default_sentinel_t)
>> >+ { return __it._M_sink->_M_discarding(); }
>>
>> I don't love using default_sentinel like this.
>>
>> Conceptually an output iterator that compares equal to a sentinel but
>> is still writable seems ... odd.
>
> For me this didn't feel odd, just what I would intuitively think of "safe" output iterator,
> that discards writes after bounds are reached.
Yes, and this is purely an internal type, not user-facing, so it's
reasonable to rely on the guarantee that writing past the sentinel is
not a bug, just discarded.
>>
>> Did you choose this API just to avoid
>> introducing a member function that isn't part of the usual iterator
>> API?
>
> Yes, but I perceived this as something natural, so I just went for it.
> I can just expose _M_discarding member in the _Sink_iter.
Let's wait for comments from anybody else, maybe others like your
solution more than I do. I don't hate it, I just wonder if it's a bit
too "clever" and so isn't obvious to readers. But maybe others will
disagree.
>>
>>
>> > };
>> >
>> > // Abstract base class for type-erased character sinks.
>> >@@ -3263,6 +3267,11 @@ namespace __format
>> > _M_bump(size_t __n)
>> > { _M_next += __n; }
>> >
>> >+ // Returns true if the _Sink is discarding incoming characters.
>> >+ virtual bool
>> >+ _M_discarding() const
>> >+ { return false; }
>> >+
>> > public:
>> > _Sink(const _Sink&) = delete;
>> > _Sink& operator=(const _Sink&) = delete;
>> >@@ -3488,6 +3497,15 @@ namespace __format
>> > _M_count += __s.size();
>> > }
>> >
>> >+ bool
>> >+ _M_discarding() const override
>> >+ {
>> >+ // format_to_n return total number of characters, that would be written,
>> >+ // thus we need to see and all characters.
>>
>> "see and all characters" seems wrong, does it mean "see and format all
>> characters"? Or somethign else?
>>
>> >+ // see https://eel.is/c%2B%2Bdraft/format#functions-22
>>
>> These links aren't necessarily permanent, so I would prefer a "dated"
>> reference to a particular standard (or working paper), e.g.
>> // see C++20 [format.functions] p20
>>
>> N.B. it was p20 in C++20, but p22 in C++23, so if you prefr to link to
>> the newer standard, then:
>>
>> // see C++23 [format.functions] p22
>>
>> >+ return false;
>> >+ }
>> >+
>> > public:
>> > [[__gnu__::__always_inline__]]
>> > explicit
>> >@@ -3550,6 +3568,15 @@ namespace __format
>> > }
>> > }
>> >
>> >+ bool
>> >+ _M_discarding() const override
>> >+ {
>> >+ // format_to_n return total number of characters, that would be written,
>> >+ // thus we need to see and all characters.
>> >+ // see https://eel.is/c%2B%2Bdraft/format#functions-22
>>
>> Same comment about "see and all" and the standard reference.
>>
>> >+ return false;
>> >+ }
>> >+
>> > typename _Sink<_CharT>::_Reservation
>> > _M_reserve(size_t __n) final
>> > {
>> >@@ -3636,17 +3663,15 @@ namespace __format
>> > template<typename _Out, typename _CharT>
>> > class _Padding_sink : public _Str_sink<_CharT>
>> > {
>> >- const size_t _M_padwidth;
>> >- const size_t _M_maxwidth;
>> >+ size_t _M_padwidth;
>> >+ size_t _M_maxwidth;
>> > _Out _M_out;
>> > size_t _M_printwidth;
>> >
>> > [[__gnu__::__always_inline__]]
>> > bool
>> > _M_ignoring() const
>> >- {
>> >- return _M_printwidth >= _M_maxwidth;
>> >- }
>> >+ { return _M_printwidth >= _M_maxwidth; }
>> >
>> > [[__gnu__::__always_inline__]]
>> > bool
>> >@@ -3659,12 +3684,21 @@ namespace __format
>> > return false;
>> > }
>> >
>> >+ void
>> >+ _M_sync_discarding()
>> >+ {
>> >+ if constexpr (is_same_v<_Out, _Sink_iter<_CharT>>)
>> >+ if (_M_out == default_sentinel)
>> >+ _M_maxwidth = _M_printwidth;
>> >+ }
>> >+
>> > void
>> > _M_flush()
>> > {
>> > span<_CharT> __new = this->_M_used();
>> > basic_string_view<_CharT> __str(__new.data(), __new.size());
>> > _M_out = __format::__write(std::move(_M_out), __str);
>> >+ _M_sync_discarding();
>> > this->_M_rewind();
>> > }
>> >
>> >@@ -3682,7 +3716,10 @@ namespace __format
>> > // We have more characters than padidng, no padding is needed,
>> > // write direclty to _M_out.
>> > if (_M_printwidth >= _M_padwidth)
>> >- _M_out = __format::__write(std::move(_M_out), __str);
>> >+ {
>> >+ _M_out = __format::__write(std::move(_M_out), __str);
>> >+ _M_sync_discarding();
>> >+ }
>> > // We reached _M_maxwidth that is smaller than _M_padwidth.
>> > // Store the prefix sequence in _M_seq, and free _M_buf.
>> > else
>> >@@ -3718,6 +3755,10 @@ namespace __format
>> > _Str_sink<_CharT>::_M_overflow();
>> > }
>> >
>> >+ bool
>> >+ _M_discarding() const override
>> >+ { return _M_ignoring(); }
>> >+
>> > typename _Sink<_CharT>::_Reservation
>> > _M_reserve(size_t __n) override
>> > {
>> >@@ -3752,15 +3793,16 @@ namespace __format
>> >
>> > public:
>> > [[__gnu__::__always_inline__]]
>> >- explicit _Padding_sink(_Out __out, size_t __padwidth)
>> >- : _M_padwidth(__padwidth), _M_maxwidth(-1),
>> >+ explicit
>> >+ _Padding_sink(_Out __out, size_t __padwidth, size_t __maxwidth)
>> >+ : _M_padwidth(__padwidth), _M_maxwidth(__maxwidth),
>> > _M_out(std::move(__out)), _M_printwidth(0)
>> >- { }
>> >+ { _M_sync_discarding(); }
>> >
>> > [[__gnu__::__always_inline__]]
>> >- explicit _Padding_sink(_Out __out, size_t __padwidth, size_t __maxwidth)
>> >- : _M_padwidth(__padwidth), _M_maxwidth(__maxwidth),
>> >- _M_out(std::move(__out)), _M_printwidth(0)
>> >+ explicit
>> >+ _Padding_sink(_Out __out, size_t __padwidth)
>> >+ : _Padding_sink(std::move(__out), __padwidth, (size_t)-1)
>> > { }
>> >
>> > _Out
>> >--
>> >2.49.0
>> >
>> >
>>
More information about the Libstdc++
mailing list