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. Did you choose this API just to avoid
introducing a member function that isn't part of the usual iterator
API?

    };

  // 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



Reply via email to