On Thu, Jul 16, 2026 at 2:05 PM Anlai Lu <[email protected]> wrote: > Use _Streambuf_sink directly in vprint_nonunicode instead of > formatting to an intermediate string (_Str_sink) and then writing > it to the stream. This avoids the temporary string allocation and > extra copy, and allows zero-copy writes into the streambuf's put > area. > > I/O errors are tracked internally by _Streambuf_sink and reported > after formatting completes via setstate(badbit), so that exceptions > from vformat (format_error, bad_alloc) propagate without setting > badbit, meeting [ostream.formatted.print]/(4.2). > Unfortunately, I have realized that this is non-conforming, as it's leads to differences in observable behavior, for example in the following two cases: print(os, "{} {}", t, u) - if t is formatted successfully but formatting of u throws, then standard requires no changes (we haven't created a string to print), but _Stream_sink may write representation of t - if formatter of t or u prints to os direclty, the standard is clear that we get the direct output from formatters and then the result of the format, but we may interleave I will post updated patch 1/4 that adds above example./
I think this optimization is worthwhile, and maybe we could reuse existing enable_nonlocking_formatter_optimziation to print direclty to ostream (we require opt-in). But that requires changes to the standard. Would be interested in working on standard proposal on such effect? > > libstdc++-v3/ChangeLog: > > * include/bits/ostream_print.h (vprint_nonunicode): Use > _Streambuf_sink instead of _Str_sink + __ostream_write. > > Signed-off-by: Anlai Lu <[email protected]> > --- > libstdc++-v3/include/bits/ostream_print.h | 12 +++++------- > 1 file changed, 5 insertions(+), 7 deletions(-) > > diff --git a/libstdc++-v3/include/bits/ostream_print.h > b/libstdc++-v3/include/bits/ostream_print.h > index 0adf16d4f..4542bfcbf 100644 > --- a/libstdc++-v3/include/bits/ostream_print.h > +++ b/libstdc++-v3/include/bits/ostream_print.h > @@ -61,21 +61,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > ostream::sentry __cerb(__os); > if (__cerb) > { > - __format::_Str_sink<char> __buf; > - std::vformat_to(__buf.out(), __os.getloc(), __fmt, __args); > - auto __out = __buf.view(); > - > + __format::_Streambuf_sink<char> __sink(__os.rdbuf()); > __try > { > - std::__ostream_write(__os, __out.data(), __out.size()); > + std::vformat_to(__sink.out(), __os.getloc(), __fmt, __args); > + std::move(__sink)._M_finish(); > } > __catch(const __cxxabiv1::__forced_unwind&) > { > __os._M_setstate(ios_base::badbit); > __throw_exception_again; > } > - __catch(...) > - { __os._M_setstate(ios_base::badbit); } > + if (__sink._M_failed()) > + __os.setstate(ios_base::badbit); > } > } > > -- > 2.34.1 > >
