On Tue, Mar 10, 2026 at 11:43 PM Jonathan Wakely <[email protected]> wrote:
> We don't want to export std::vprint_unicode etc. from libstdc++.so yet, > but we can give users the option of improving compile times by getting > the definitions of the std::print internals from libstdc++exp.a instead. > > This commit adds a macro, _GLIBCXX_NO_INLINE_PRINT, which disables the > inline definitions of std::vprint_unicode etc. so that extern > definitions in libstdc++exp.a can be used instead. > > With this change compiling a helloworld using std::print goes from 6s to > under 2s with trunk (compared to almost 3s with a GCC 15.2.1 build > using --enable-checking=release). > > libstdc++-v3/ChangeLog: > > PR libstdc++/124410 > * doc/html/*: Regenerate. > * doc/xml/manual/using.xml (_GLIBCXX_NO_INLINE_PRINT): Document > macro. > * include/Makefile.am: Add bits/print.h and bits/ostream_print.h > headers. > * include/Makefile.in: Regenerate. > * include/std/ostream (vprint_nonunicode, vprint_unicode): Move > definitions to new bits/ostream_print.h header. > * include/std/print (__format::_File_sink, vprint_nonunicode) > (vprint_nonunicode_buffered, vprint_unicode) > (vprint_unicode_buffered): Move definitions to new bits/print.h > header. > * src/c++23/print.cc: Include new headers to define symbols for > inline print functions. > * include/bits/ostream_print.h: New file. > * include/bits/print.h: New file. > --- > > The std::print tests pass (with and without the new macro defined). > I'm running he full testsuite now. > Assuming it passed, this loooks good to me. TDIL what [[gnu::used]] attribute could be used for. One suggestion below, that maybe we could record as the comment. > > libstdc++-v3/doc/html/index.html | 4 +- > libstdc++-v3/doc/html/manual/using.html | 2 + > .../doc/html/manual/using_macros.html | 5 + > libstdc++-v3/doc/xml/manual/using.xml | 11 + > libstdc++-v3/include/Makefile.am | 2 + > libstdc++-v3/include/Makefile.in | 2 + > libstdc++-v3/include/bits/ostream_print.h | 161 +++++++++ > libstdc++-v3/include/bits/print.h | 339 ++++++++++++++++++ > libstdc++-v3/include/std/ostream | 112 +----- > libstdc++-v3/include/std/print | 295 ++------------- > libstdc++-v3/src/c++23/print.cc | 7 + > 11 files changed, 575 insertions(+), 365 deletions(-) > create mode 100644 libstdc++-v3/include/bits/ostream_print.h > create mode 100644 libstdc++-v3/include/bits/print.h > > diff --git a/libstdc++-v3/doc/html/index.html > b/libstdc++-v3/doc/html/index.html > index 01d7bb787dce..782f3bbc299c 100644 > --- a/libstdc++-v3/doc/html/index.html > +++ b/libstdc++-v3/doc/html/index.html > @@ -1,6 +1,6 @@ > <?xml version="1.0" encoding="UTF-8" standalone="no"?> > -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " > http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns=" > http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" > content="text/html; charset=UTF-8" /><title>The GNU C++ > Library</title><meta name="generator" content="DocBook XSL Stylesheets > Vsnapshot" /><meta name="description" content="Short Contents Copyright (C) > 2008-2025 FSF Permission is granted to copy, distribute and/or modify this > document under the terms of the GNU Free Documentation License, Version 1.2 > or any later version published by the Free Software Foundation; with no > Invariant Sections, with no Front-Cover Texts, and with no Back-Cover > Texts. This is the top level of the libstdc++ documentation set. The > documentation is divided into the following three sections. Manual > Frequently Asked Questions API and Source Documentation" /><meta > name="keywords" content="ISO C++, runtime, library" /><link rel="home" > href="index.html" title="The GNU C++ Library" /><link rel="next" > href="manual/index.html" title="The GNU C++ Library Manual" > /></head><body><div class="navheader"><table width="100%" > summary="Navigation header"><tr><th colspan="3" align="center">The GNU C++ > Library</th></tr><tr><td width="20%" align="left"> </td><th width="60%" > align="center"> </th><td width="20%" align="right"> <a accesskey="n" > href="manual/index.html">Next</a></td></tr></table><hr /></div><div > class="set" lang="en" xml:lang="en"><div class="titlepage"><div><div><h1 > class="title"><a id="set-index"></a>The GNU C++ Library</h1></div><div><div > class="abstract"><a id="contents"></a><p class="title"><strong>Short > Contents</strong></p><p> > - Copyright (C) 2008-2025 > +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " > http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns=" > http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" > content="text/html; charset=UTF-8" /><title>The GNU C++ > Library</title><meta name="generator" content="DocBook XSL Stylesheets > Vsnapshot" /><meta name="description" content="Short Contents Copyright (C) > 2008-2026 FSF Permission is granted to copy, distribute and/or modify this > document under the terms of the GNU Free Documentation License, Version 1.2 > or any later version published by the Free Software Foundation; with no > Invariant Sections, with no Front-Cover Texts, and with no Back-Cover > Texts. This is the top level of the libstdc++ documentation set. The > documentation is divided into the following three sections. Manual > Frequently Asked Questions API and Source Documentation" /><meta > name="keywords" content="ISO C++, runtime, library" /><link rel="home" > href="index.html" title="The GNU C++ Library" /><link rel="next" > href="manual/index.html" title="The GNU C++ Library Manual" > /></head><body><div class="navheader"><table width="100%" > summary="Navigation header"><tr><th colspan="3" align="center">The GNU C++ > Library</th></tr><tr><td width="20%" align="left"> </td><th width="60%" > align="center"> </th><td width="20%" align="right"> <a accesskey="n" > href="manual/index.html">Next</a></td></tr></table><hr /></div><div > class="set" lang="en" xml:lang="en"><div class="titlepage"><div><div><h1 > class="title"><a id="set-index"></a>The GNU C++ Library</h1></div><div><div > class="abstract"><a id="contents"></a><p class="title"><strong>Short > Contents</strong></p><p> > + Copyright (C) 2008-2026 > <a class="link" href="https://www.fsf.org" target="_top">FSF > </a> > </p><p> > diff --git a/libstdc++-v3/doc/html/manual/using.html > b/libstdc++-v3/doc/html/manual/using.html > index fcd1b96de0d7..27f503ecaccc 100644 > --- a/libstdc++-v3/doc/html/manual/using.html > +++ b/libstdc++-v3/doc/html/manual/using.html > @@ -34,6 +34,8 @@ > is required for use of experimental C++ library features. > This currently provides support for the C++23 types defined in the > <code class="filename"><stacktrace></code> header, > + the C++23 functions defined in the > + <code class="filename"><print></code> header, > the Filesystem library extensions defined in the > <code class="filename"><experimental/filesystem></code> > header, > diff --git a/libstdc++-v3/doc/html/manual/using_macros.html > b/libstdc++-v3/doc/html/manual/using_macros.html > index b1d05d99d760..60f2f02c75bd 100644 > --- a/libstdc++-v3/doc/html/manual/using_macros.html > +++ b/libstdc++-v3/doc/html/manual/using_macros.html > @@ -147,4 +147,9 @@ > mode will revert to the non-conforming implementation used prior > to the > <a class="link" href=" > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112591" > target="_top">PR112591</a> > in GCC-16. Has no impact for C++20 or later modes. > + </p></dd><dt><span class="term"><code > class="code">_GLIBCXX_NO_INLINE_PRINT</code></span></dt><dd><p> > + Undefined by default. When defined <code > class="code">std::print</code> and > + <code class="code">std::println</code> are not defined using > inline functions. > + This means that code using those functions will compile faster, > + but the <code class="option">-lstdc++exp</code> must be used when > linking. > </p></dd></dl></div></div><div class="navfooter"><hr /><table > width="100%" summary="Navigation footer"><tr><td width="40%" > align="left"><a accesskey="p" href="using_headers.html">Prev</a> </td><td > width="20%" align="center"><a accesskey="u" > href="using.html">Up</a></td><td width="40%" align="right"> <a > accesskey="n" href="using_dual_abi.html">Next</a></td></tr><tr><td > width="40%" align="left" valign="top">Headers </td><td width="20%" > align="center"><a accesskey="h" href="../index.html">Home</a></td><td > width="40%" align="right" valign="top"> Dual > ABI</td></tr></table></div></body></html> > \ No newline at end of file > diff --git a/libstdc++-v3/doc/xml/manual/using.xml > b/libstdc++-v3/doc/xml/manual/using.xml > index 8adfecfe54ba..b505775a6a71 100644 > --- a/libstdc++-v3/doc/xml/manual/using.xml > +++ b/libstdc++-v3/doc/xml/manual/using.xml > @@ -83,6 +83,8 @@ > is required for use of experimental C++ library features. > This currently provides support for the C++23 types defined in the > <filename class="headerfile"><stacktrace></filename> header, > + the C++23 functions defined in the > + <filename class="headerfile"><print></filename> header, > the Filesystem library extensions defined in the > <filename > class="headerfile"><experimental/filesystem></filename> > header, > @@ -1363,6 +1365,15 @@ g++ -Winvalid-pch -I. -include stdc++.h -H -g -O2 > hello.cc -o test.exe > in GCC-16. Has no impact for C++20 or later modes. > </para> > </listitem></varlistentry> > + <varlistentry><term><code>_GLIBCXX_NO_INLINE_PRINT</code></term> > + <listitem> > + <para> > + Undefined by default. When defined <code>std::print</code> and > + <code>std::println</code> are not defined using inline functions. > + This means that code using those functions will compile faster, > + but the <option>-lstdc++exp</option> must be used when linking. > + </para> > + </listitem></varlistentry> > </variablelist> > > </section> > diff --git a/libstdc++-v3/include/Makefile.am > b/libstdc++-v3/include/Makefile.am > index e6ac312ac7a4..f49864d62ee0 100644 > --- a/libstdc++-v3/include/Makefile.am > +++ b/libstdc++-v3/include/Makefile.am > @@ -243,7 +243,9 @@ bits_headers = \ > ${bits_srcdir}/node_handle.h \ > ${bits_srcdir}/ostream.tcc \ > ${bits_srcdir}/ostream_insert.h \ > + ${bits_srcdir}/ostream_print.h \ > ${bits_srcdir}/postypes.h \ > + ${bits_srcdir}/print.h \ > ${bits_srcdir}/quoted_string.h \ > ${bits_srcdir}/random.h \ > ${bits_srcdir}/random.tcc \ > diff --git a/libstdc++-v3/include/Makefile.in > b/libstdc++-v3/include/Makefile.in > index b2e40c4d313f..c9be21e94376 100644 > --- a/libstdc++-v3/include/Makefile.in > +++ b/libstdc++-v3/include/Makefile.in > @@ -599,7 +599,9 @@ bits_freestanding = \ > @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/node_handle.h \ > @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/ostream.tcc \ > @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/ostream_insert.h \ > +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/ostream_print.h \ > @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/postypes.h \ > +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/print.h \ > @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/quoted_string.h \ > @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/random.h \ > @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/random.tcc \ > diff --git a/libstdc++-v3/include/bits/ostream_print.h > b/libstdc++-v3/include/bits/ostream_print.h > new file mode 100644 > index 000000000000..0adf16d4fe3e > --- /dev/null > +++ b/libstdc++-v3/include/bits/ostream_print.h > @@ -0,0 +1,161 @@ > +// Inline implementation details for std::print functions -*- C++ -*- > + > +// Copyright The GNU Toolchain Authors. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// Under Section 7 of GPL version 3, you are granted additional > +// permissions described in the GCC Runtime Library Exception, version > +// 3.1, as published by the Free Software Foundation. > + > +// You should have received a copy of the GNU General Public License and > +// a copy of the GCC Runtime Library Exception along with this program; > +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see > +// <http://www.gnu.org/licenses/>. > + > +/** @file include/bits/ostream_print.h > + * This is an internal header file, included by other library headers. > + * Do not attempt to use it directly. @headername{ostream} > + * > + * This file contains the parts of `<ostream>` which are currently > defined > + * inline, but should be moved into the library eventually. > + */ > + > +#ifndef _GLIBCXX_OSTREAM_PRINT_H > +#define _GLIBCXX_OSTREAM_PRINT_H 1 > + > +#ifdef _GLIBCXX_SYSHDR > +#pragma GCC system_header > +#endif > + > +#include <bits/requires_hosted.h> // for std::format > + > +#include <bits/version.h> > + > +#ifdef __glibcxx_print // C++ >= 23 > +#include <format> > + > +namespace std _GLIBCXX_VISIBILITY(default) > +{ > +_GLIBCXX_BEGIN_NAMESPACE_VERSION > + > +#ifdef _GLIBCXX_NO_INLINE_PRINT > +# define _GLIBCXX_PRINT_INLINE_USED [[__gnu__::__used__]] > +#else > +# define _GLIBCXX_PRINT_INLINE_USED > +#endif > + > + _GLIBCXX_PRINT_INLINE_USED > + inline void > + vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args) > + { > + ostream::sentry __cerb(__os); > + if (__cerb) > + { > + __format::_Str_sink<char> __buf; > + std::vformat_to(__buf.out(), __os.getloc(), __fmt, __args); > + auto __out = __buf.view(); > + > + __try > + { > + std::__ostream_write(__os, __out.data(), __out.size()); > + } > + __catch(const __cxxabiv1::__forced_unwind&) > + { > + __os._M_setstate(ios_base::badbit); > + __throw_exception_again; > + } > + __catch(...) > + { __os._M_setstate(ios_base::badbit); } > + } > + } > + > + _GLIBCXX_PRINT_INLINE_USED > + inline void > + vprint_unicode(ostream& __os, string_view __fmt, format_args __args) > + { > +#if !defined(_WIN32) || defined(__CYGWIN__) > + // For most targets we don't need to do anything special to write > + // Unicode to a terminal. > + std::vprint_nonunicode(__os, __fmt, __args); > +#else > + ostream::sentry __cerb(__os); > + if (__cerb) > + { > + __format::_Str_sink<char> __buf; > + std::vformat_to(__buf.out(), __os.getloc(), __fmt, __args); > + auto __out = __buf._M_span(); > + > + void* __open_terminal(streambuf*); > + error_code __write_to_terminal(void*, span<char>); > + // If stream refers to a terminal, write a Unicode string to it. > + if (auto __term = __open_terminal(__os.rdbuf())) > + { > +#if !defined(_WIN32) || defined(__CYGWIN__) > + // For POSIX, __open_terminal(streambuf*) uses fdopen to open a > + // new file, so we would need to close it here. This code is > not > + // actually compiled because it's inside an #ifdef _WIN32 > group, > + // but just in case that changes in future ... > + struct _Guard > + { > + _Guard(void* __p) : _M_f((FILE*)__p) { } > + ~_Guard() { std::fclose(_M_f); } > + _Guard(_Guard&&) = delete; > + _Guard& operator=(_Guard&&) = delete; > + FILE* _M_f; > + }; > + _Guard __g(__term); > +#endif > + > + ios_base::iostate __err = ios_base::goodbit; > + __try > + { > + if (__os.rdbuf()->pubsync() == -1) > + __err = ios::badbit; > + else if (auto __e = __write_to_terminal(__term, __out)) > + if (__e != > std::make_error_code(errc::illegal_byte_sequence)) > + __err = ios::badbit; > + } > + __catch(const __cxxabiv1::__forced_unwind&) > + { > + __os._M_setstate(ios_base::badbit); > + __throw_exception_again; > + } > + __catch(...) > + { __os._M_setstate(ios_base::badbit); } > + > + if (__err) > + __os.setstate(__err); > + return; > + } > + > + // Otherwise just insert the string as vprint_nonunicode does. > + __try > + { > + std::__ostream_write(__os, __out.data(), __out.size()); > + } > + __catch(const __cxxabiv1::__forced_unwind&) > + { > + __os._M_setstate(ios_base::badbit); > + __throw_exception_again; > + } > + __catch(...) > + { __os._M_setstate(ios_base::badbit); } > + } > +#endif // _WIN32 > + } > +#undef _GLIBCXX_PRINT_INLINE_USED > + > +_GLIBCXX_END_NAMESPACE_VERSION > +} // namespace std > +#endif // __glibcxx_print > +#endif // _GLIBCXX_OSTREAM_PRINT_H > diff --git a/libstdc++-v3/include/bits/print.h > b/libstdc++-v3/include/bits/print.h > new file mode 100644 > index 000000000000..67a5a1729135 > --- /dev/null > +++ b/libstdc++-v3/include/bits/print.h > @@ -0,0 +1,339 @@ > +// Inline implementation details for std::print functions -*- C++ -*- > + > +// Copyright The GNU Toolchain Authors. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// Under Section 7 of GPL version 3, you are granted additional > +// permissions described in the GCC Runtime Library Exception, version > +// 3.1, as published by the Free Software Foundation. > + > +// You should have received a copy of the GNU General Public License and > +// a copy of the GCC Runtime Library Exception along with this program; > +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see > +// <http://www.gnu.org/licenses/>. > + > +/** @file include/bits/print.h > + * This is an internal header file, included by other library headers. > + * Do not attempt to use it directly. @headername{print} > + * > + * This file contains the parts of `<print>` which are currently defined > + * inline, but should be moved into the library eventually. > + */ > + > +#ifndef _GLIBCXX_PRINT_H > +#define _GLIBCXX_PRINT_H 1 > + > +#ifdef _GLIBCXX_SYSHDR > +#pragma GCC system_header > +#endif > + > +#include <bits/requires_hosted.h> // for std::format > + > +#include <bits/version.h> > + > +#ifdef __glibcxx_print // C++ >= 23 > + > +#include <format> > +#include <cstdio> // FILE, EOF, flockfile, etc. > +#include <cerrno> // EACCES, EIO > +#include <bits/functexcept.h> // __throw_system_error > + > +#ifdef _WIN32 > +# include <system_error> // system_error > +#endif > + > +namespace std _GLIBCXX_VISIBILITY(default) > +{ > +_GLIBCXX_BEGIN_NAMESPACE_VERSION > + > +namespace __format > +{ > +#if _GLIBCXX_USE_STDIO_LOCKING && _GLIBCXX_USE_GLIBC_STDIO_EXT > + // These are defined in <stdio_ext.h> but we don't want to include that. > + extern "C" int __fwritable(FILE*) noexcept; > + extern "C" int __flbf(FILE*) noexcept; > + extern "C" size_t __fbufsize(FILE*) noexcept; > + > + // A format sink that writes directly to a Glibc FILE. > + // The file is locked on construction and its buffer is accessed > directly. > + class _File_sink final : _Buf_sink<char> > + { > + struct _File > + { > + explicit > + _File(FILE* __f) : _M_file(__f) > + { > + ::flockfile(__f); > + // Ensure stream is in write mode > + if (!__fwritable(__f)) > + { > + ::funlockfile(__f); > + __throw_system_error(EACCES); > + } > + // Allocate buffer if needed: > + if (_M_write_buf().empty()) > + if (::__overflow(__f, EOF) == EOF) > + { > + const int __err = errno; > + ::funlockfile(__f); > + __throw_system_error(__err); > + } > + } > + > + ~_File() { ::funlockfile(_M_file); } > + > + _File(_File&&) = delete; > + > + // A span viewing the unused portion of the stream's output buffer. > + std::span<char> > + _M_write_buf() noexcept > + { > + return {_M_file->_IO_write_ptr, > + size_t(_M_file->_IO_buf_end - _M_file->_IO_write_ptr)}; > + } > + > + // Flush the output buffer to the file so we can write to it again. > + void > + _M_flush() > + { > + if (::fflush_unlocked(_M_file)) > + __throw_system_error(errno); > + } > + > + // Update the current position in the output buffer. > + void > + _M_bump(size_t __n) noexcept > + { _M_file->_IO_write_ptr += __n; } > + > + bool > + _M_line_buffered() const noexcept > + { return __flbf(_M_file); } // Or: _M_file->_flags & 0x200 > + > + bool > + _M_unbuffered() const noexcept > + { return __fbufsize(_M_file) == 1; } // Or: _M_file->_flags & 0x2 > + > + FILE* _M_file; > + } _M_file; > + > + bool _M_add_newline; // True for std::println, false for std::print. > + > + // Flush the stream's put area so it can be refilled. > + void > + _M_overflow() override > + { > + auto __s = this->_M_used(); > + if (__s.data() == this->_M_buf) > + { > + // Characters in internal buffer need to be transferred to the > FILE. > + auto __n = ::fwrite_unlocked(__s.data(), 1, __s.size(), > + _M_file._M_file); > + if (__n != __s.size()) > + __throw_system_error(errno); > + this->_M_reset(this->_M_buf); > + } > + else > + { > + // Characters were written directly to the FILE's output buffer. > + _M_file._M_bump(__s.size()); > + _M_file._M_flush(); > + this->_M_reset(_M_file._M_write_buf()); > + } > + } > + > + public: > + _File_sink(FILE* __f, bool __add_newline) > + : _M_file(__f), _M_add_newline(__add_newline) > + { > + if (!_M_file._M_unbuffered()) > + // Write directly to the FILE's output buffer. > + this->_M_reset(_M_file._M_write_buf()); > + } > + > + ~_File_sink() noexcept(false) > + { > + auto __s = this->_M_used(); > + if (__s.data() == this->_M_buf) // Unbuffered stream > + { > + _File_sink::_M_overflow(); > + if (_M_add_newline) > + ::putc_unlocked('\n', _M_file._M_file); > + } > + else > + { > + _M_file._M_bump(__s.size()); > + if (_M_add_newline) > + ::putc_unlocked('\n', _M_file._M_file); > + else if (_M_file._M_line_buffered() && __s.size() > + && (__s.back() == '\n' > + || __builtin_memchr(__s.data(), '\n', > __s.size()))) > + _M_file._M_flush(); > + } > + } > + > + using _Sink<char>::out; > + }; > +#elif _GLIBCXX_USE_STDIO_LOCKING > + // A format sink that buffers output and then copies it to a stdio FILE. > + // The file is locked on construction and written to using > fwrite_unlocked. > + class _File_sink final : _Buf_sink<char> > + { > + FILE* _M_file; > + bool _M_add_newline; > + > + // Transfer buffer contents to the FILE, so buffer can be refilled. > + void > + _M_overflow() override > + { > + auto __s = this->_M_used(); > +#if _GLIBCXX_HAVE_FWRITE_UNLOCKED > + auto __n = ::fwrite_unlocked(__s.data(), 1, __s.size(), _M_file); > + if (__n != __s.size()) > + __throw_system_error(errno); > +#else > + for (char __c : __s) > + ::putc_unlocked(__c, _M_file); > + if (::ferror(_M_file)) > + __throw_system_error(errno); > +#endif > + this->_M_reset(this->_M_buf); > + } > + > + public: > + _File_sink(FILE* __f, bool __add_newline) noexcept > + : _Buf_sink<char>(), _M_file(__f), _M_add_newline(__add_newline) > + { ::flockfile(__f); } > + > + ~_File_sink() noexcept(false) > + { > + _File_sink::_M_overflow(); > + if (_M_add_newline) > + ::putc_unlocked('\n', _M_file); > + ::funlockfile(_M_file); > + } > + > + using _Sink<char>::out; > + }; > +#else > + // A wrapper around a format sink that copies the output to a stdio > FILE. > + // This is not actually a _Sink itself, but it creates one to hold the > + // formatted characters and then copies them to the file when finished. > + class _File_sink final > + { > + FILE* _M_file; > + _Str_sink<char> _M_sink; > + bool _M_add_newline; > + > + public: > + _File_sink(FILE* __f, bool __add_newline) noexcept > + : _M_file(__f), _M_add_newline(__add_newline) > + { } > + > + ~_File_sink() noexcept(false) > + { > + string __s = std::move(_M_sink).get(); > + if (_M_add_newline) > + __s += '\n'; > + auto __n = std::fwrite(__s.data(), 1, __s.size(), _M_file); > + if (__n < __s.size()) > + __throw_system_error(EIO); > + } > + > + auto out() { return _M_sink.out(); } > + }; > +#endif > +} // namespace __format > + > +#ifdef _GLIBCXX_NO_INLINE_PRINT > +# define _GLIBCXX_PRINT_INLINE_USED [[__gnu__::__used__]] > +#else > +# define _GLIBCXX_PRINT_INLINE_USED > +#endif > + > + _GLIBCXX_PRINT_INLINE_USED > + inline void > + vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args) > + { > + std::vformat_to(__format::_File_sink(__stream, false).out(), __fmt, > __args); > + } > + > + _GLIBCXX_PRINT_INLINE_USED > + inline void > + vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, > + format_args __args) > + { > + __format::_Str_sink<char> __buf; > + std::vformat_to(__buf.out(), __fmt, __args); > + auto __out = __buf.view(); > + if (std::fwrite(__out.data(), 1, __out.size(), __stream) != > __out.size()) > + __throw_system_error(EIO); > + } > + > + _GLIBCXX_PRINT_INLINE_USED > + inline void > + vprint_unicode(FILE* __stream, string_view __fmt, format_args __args) > + { > +#if !defined(_WIN32) || defined(__CYGWIN__) > + // For most targets we don't need to do anything special to write > + // Unicode to a terminal. > + std::vprint_nonunicode(__stream, __fmt, __args); > +#else > + __format::_Str_sink<char> __buf; > + std::vformat_to(__buf.out(), __fmt, __args); > + auto __out = __buf._M_span(); > + > + void* __open_terminal(FILE*); > + error_code __write_to_terminal(void*, span<char>); > + // If stream refers to a terminal, write a native Unicode string to > it. > + if (auto __term = __open_terminal(__stream)) > + { > + error_code __e; > + if (!std::fflush(__stream)) > + { > + __e = __write_to_terminal(__term, __out); > + if (!__e) > + return; > + if (__e == std::make_error_code(errc::illegal_byte_sequence)) > + return; > + } > + else > + __e = error_code(errno, generic_category()); > + _GLIBCXX_THROW_OR_ABORT(system_error(__e, "std::vprint_unicode")); > + } > + > + // Otherwise just write the string to the file. > + if (std::fwrite(__out.data(), 1, __out.size(), __stream) != > __out.size()) > + __throw_system_error(EIO); > +#endif > + } > + > + _GLIBCXX_PRINT_INLINE_USED > + inline void > + vprint_unicode_buffered(FILE* __stream, string_view __fmt, format_args > __args) > + { > +#if !defined(_WIN32) || defined(__CYGWIN__) > + // For most targets we don't need to do anything special to write > + // Unicode to a terminal. Just use the nonunicode function. > + std::vprint_nonunicode_buffered(__stream, __fmt, __args); > +#else > + // For Windows the locking function formats everything first anyway, > + // so no formatting happens while a lock is taken. Just use that. > + std::vprint_unicode(__stream, __fmt, __args); > +#endif > + } > +#undef _GLIBCXX_PRINT_INLINE_USED > + > +_GLIBCXX_END_NAMESPACE_VERSION > +} // namespace std > +#endif // __glibcxx_print > +#endif // _GLIBCXX_PRINT_H > diff --git a/libstdc++-v3/include/std/ostream > b/libstdc++-v3/include/std/ostream > index e8dcfda2682f..b99c0f92d345 100644 > --- a/libstdc++-v3/include/std/ostream > +++ b/libstdc++-v3/include/std/ostream > @@ -40,12 +40,17 @@ > #include <bits/requires_hosted.h> // iostreams > > #include <bits/ostream.h> > -#if __cplusplus > 202002L > -# include <format> > + > +#ifdef __glibcxx_print > +# include <format> // format_string, make_format_args > +#endif > + > +#ifndef _GLIBCXX_NO_INLINE_PRINT > +# include <bits/ostream_print.h> > #endif > > # define __glibcxx_want_print > -#include <bits/version.h> // __glibcxx_syncbuf > +#include <bits/version.h> // __cpp_lib_print, __glibcxx_syncbuf > > namespace std _GLIBCXX_VISIBILITY(default) > { > @@ -156,103 +161,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif // __glibcxx_syncbuf > > #if __cpp_lib_print // C++ >= 23 > - inline void > - vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args) > - { > - ostream::sentry __cerb(__os); > - if (__cerb) > - { > - __format::_Str_sink<char> __buf; > - std::vformat_to(__buf.out(), __os.getloc(), __fmt, __args); > - auto __out = __buf.view(); > + void > + vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args); > > - __try > - { > - std::__ostream_write(__os, __out.data(), __out.size()); > - } > - __catch(const __cxxabiv1::__forced_unwind&) > - { > - __os._M_setstate(ios_base::badbit); > - __throw_exception_again; > - } > - __catch(...) > - { __os._M_setstate(ios_base::badbit); } > - } > - } > - > - inline void > - vprint_unicode(ostream& __os, string_view __fmt, format_args __args) > - { > -#if !defined(_WIN32) || defined(__CYGWIN__) > - // For most targets we don't need to do anything special to write > - // Unicode to a terminal. > - std::vprint_nonunicode(__os, __fmt, __args); > -#else > - ostream::sentry __cerb(__os); > - if (__cerb) > - { > - __format::_Str_sink<char> __buf; > - std::vformat_to(__buf.out(), __os.getloc(), __fmt, __args); > - auto __out = __buf._M_span(); > - > - void* __open_terminal(streambuf*); > - error_code __write_to_terminal(void*, span<char>); > - // If stream refers to a terminal, write a Unicode string to it. > - if (auto __term = __open_terminal(__os.rdbuf())) > - { > -#if !defined(_WIN32) || defined(__CYGWIN__) > - // For POSIX, __open_terminal(streambuf*) uses fdopen to open a > - // new file, so we would need to close it here. This code is > not > - // actually compiled because it's inside an #ifdef _WIN32 > group, > - // but just in case that changes in future ... > - struct _Guard > - { > - _Guard(void* __p) : _M_f((FILE*)__p) { } > - ~_Guard() { std::fclose(_M_f); } > - _Guard(_Guard&&) = delete; > - _Guard& operator=(_Guard&&) = delete; > - FILE* _M_f; > - }; > - _Guard __g(__term); > -#endif > - > - ios_base::iostate __err = ios_base::goodbit; > - __try > - { > - if (__os.rdbuf()->pubsync() == -1) > - __err = ios::badbit; > - else if (auto __e = __write_to_terminal(__term, __out)) > - if (__e != > std::make_error_code(errc::illegal_byte_sequence)) > - __err = ios::badbit; > - } > - __catch(const __cxxabiv1::__forced_unwind&) > - { > - __os._M_setstate(ios_base::badbit); > - __throw_exception_again; > - } > - __catch(...) > - { __os._M_setstate(ios_base::badbit); } > - > - if (__err) > - __os.setstate(__err); > - return; > - } > - > - // Otherwise just insert the string as vprint_nonunicode does. > - __try > - { > - std::__ostream_write(__os, __out.data(), __out.size()); > - } > - __catch(const __cxxabiv1::__forced_unwind&) > - { > - __os._M_setstate(ios_base::badbit); > - __throw_exception_again; > - } > - __catch(...) > - { __os._M_setstate(ios_base::badbit); } > - } > -#endif // _WIN32 > - } > + void > + vprint_unicode(ostream& __os, string_view __fmt, format_args __args); > > template<typename... _Args> > inline void > @@ -294,7 +207,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif > __os.put('\n'); > } > - > #endif // __cpp_lib_print > > _GLIBCXX_END_NAMESPACE_VERSION > diff --git a/libstdc++-v3/include/std/print > b/libstdc++-v3/include/std/print > index 6ffd9a4b1b3f..767dadf3a8b8 100644 > --- a/libstdc++-v3/include/std/print > +++ b/libstdc++-v3/include/std/print > @@ -40,284 +40,42 @@ > > #ifdef __cpp_lib_print // C++ >= 23 > > -#include <format> > -#include <cstdio> > -#include <cerrno> > -#include <bits/functexcept.h> > +#include <format> // format_args (TODO: move to > bits/formatfwd.h?) > +#include <cstdio> // FILE, EOF, putc > +#include <cerrno> // EACCES, EIO > +#include <bits/functexcept.h> // __throw_system_error > > #ifdef _WIN32 > # include <system_error> > #endif > > +#ifndef _GLIBCXX_NO_INLINE_PRINT > +# include <bits/print.h> > +#endif > + > namespace std _GLIBCXX_VISIBILITY(default) > { > _GLIBCXX_BEGIN_NAMESPACE_VERSION > > -namespace __format > -{ > -#if _GLIBCXX_USE_STDIO_LOCKING && _GLIBCXX_USE_GLIBC_STDIO_EXT > - // These are defined in <stdio_ext.h> but we don't want to include that. > - extern "C" int __fwritable(FILE*) noexcept; > - extern "C" int __flbf(FILE*) noexcept; > - extern "C" size_t __fbufsize(FILE*) noexcept; > + void > + vprint_nonunicode(FILE* __stream, string_view __fmt, format_args > __args); > > - // A format sink that writes directly to a Glibc FILE. > - // The file is locked on construction and its buffer is accessed > directly. > - class _File_sink final : _Buf_sink<char> > - { > - struct _File > - { > - explicit > - _File(FILE* __f) : _M_file(__f) > - { > - ::flockfile(__f); > - // Ensure stream is in write mode > - if (!__fwritable(__f)) > - { > - ::funlockfile(__f); > - __throw_system_error(EACCES); > - } > - // Allocate buffer if needed: > - if (_M_write_buf().empty()) > - if (::__overflow(__f, EOF) == EOF) > - { > - const int __err = errno; > - ::funlockfile(__f); > - __throw_system_error(__err); > - } > - } > - > - ~_File() { ::funlockfile(_M_file); } > - > - _File(_File&&) = delete; > - > - // A span viewing the unused portion of the stream's output buffer. > - std::span<char> > - _M_write_buf() noexcept > - { > - return {_M_file->_IO_write_ptr, > - size_t(_M_file->_IO_buf_end - _M_file->_IO_write_ptr)}; > - } > - > - // Flush the output buffer to the file so we can write to it again. > - void > - _M_flush() > - { > - if (::fflush_unlocked(_M_file)) > - __throw_system_error(errno); > - } > - > - // Update the current position in the output buffer. > - void > - _M_bump(size_t __n) noexcept > - { _M_file->_IO_write_ptr += __n; } > - > - bool > - _M_line_buffered() const noexcept > - { return __flbf(_M_file); } // Or: _M_file->_flags & 0x200 > - > - bool > - _M_unbuffered() const noexcept > - { return __fbufsize(_M_file) == 1; } // Or: _M_file->_flags & 0x2 > - > - FILE* _M_file; > - } _M_file; > - > - bool _M_add_newline; // True for std::println, false for std::print. > - > - // Flush the stream's put area so it can be refilled. > - void > - _M_overflow() override > - { > - auto __s = this->_M_used(); > - if (__s.data() == this->_M_buf) > - { > - // Characters in internal buffer need to be transferred to the > FILE. > - auto __n = ::fwrite_unlocked(__s.data(), 1, __s.size(), > - _M_file._M_file); > - if (__n != __s.size()) > - __throw_system_error(errno); > - this->_M_reset(this->_M_buf); > - } > - else > - { > - // Characters were written directly to the FILE's output buffer. > - _M_file._M_bump(__s.size()); > - _M_file._M_flush(); > - this->_M_reset(_M_file._M_write_buf()); > - } > - } > - > - public: > - _File_sink(FILE* __f, bool __add_newline) > - : _M_file(__f), _M_add_newline(__add_newline) > - { > - if (!_M_file._M_unbuffered()) > - // Write directly to the FILE's output buffer. > - this->_M_reset(_M_file._M_write_buf()); > - } > - > - ~_File_sink() noexcept(false) > - { > - auto __s = this->_M_used(); > - if (__s.data() == this->_M_buf) // Unbuffered stream > - { > - _File_sink::_M_overflow(); > - if (_M_add_newline) > - ::putc_unlocked('\n', _M_file._M_file); > - } > - else > - { > - _M_file._M_bump(__s.size()); > - if (_M_add_newline) > - ::putc_unlocked('\n', _M_file._M_file); > - else if (_M_file._M_line_buffered() && __s.size() > - && (__s.back() == '\n' > - || __builtin_memchr(__s.data(), '\n', > __s.size()))) > - _M_file._M_flush(); > - } > - } > - > - using _Sink<char>::out; > - }; > -#elif _GLIBCXX_USE_STDIO_LOCKING > - // A format sink that buffers output and then copies it to a stdio FILE. > - // The file is locked on construction and written to using > fwrite_unlocked. > - class _File_sink final : _Buf_sink<char> > - { > - FILE* _M_file; > - bool _M_add_newline; > - > - // Transfer buffer contents to the FILE, so buffer can be refilled. > - void > - _M_overflow() override > - { > - auto __s = this->_M_used(); > -#if _GLIBCXX_HAVE_FWRITE_UNLOCKED > - auto __n = ::fwrite_unlocked(__s.data(), 1, __s.size(), _M_file); > - if (__n != __s.size()) > - __throw_system_error(errno); > -#else > - for (char __c : __s) > - ::putc_unlocked(__c, _M_file); > - if (::ferror(_M_file)) > - __throw_system_error(errno); > -#endif > - this->_M_reset(this->_M_buf); > - } > - > - public: > - _File_sink(FILE* __f, bool __add_newline) noexcept > - : _Buf_sink<char>(), _M_file(__f), _M_add_newline(__add_newline) > - { ::flockfile(__f); } > - > - ~_File_sink() noexcept(false) > - { > - _File_sink::_M_overflow(); > - if (_M_add_newline) > - ::putc_unlocked('\n', _M_file); > - ::funlockfile(_M_file); > - } > - > - using _Sink<char>::out; > - }; > -#else > - // A wrapper around a format sink that copies the output to a stdio > FILE. > - // This is not actually a _Sink itself, but it creates one to hold the > - // formatted characters and then copies them to the file when finished. > - class _File_sink final > - { > - FILE* _M_file; > - _Str_sink<char> _M_sink; > - bool _M_add_newline; > - > - public: > - _File_sink(FILE* __f, bool __add_newline) noexcept > - : _M_file(__f), _M_add_newline(__add_newline) > - { } > - > - ~_File_sink() noexcept(false) > - { > - string __s = std::move(_M_sink).get(); > - if (_M_add_newline) > - __s += '\n'; > - auto __n = std::fwrite(__s.data(), 1, __s.size(), _M_file); > - if (__n < __s.size()) > - __throw_system_error(EIO); > - } > - > - auto out() { return _M_sink.out(); } > - }; > -#endif > -} // namespace __format > - > - inline void > - vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args) > - { > - std::vformat_to(__format::_File_sink(__stream, false).out(), __fmt, > __args); > - } > - > - inline void > + void > vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, > - format_args __args) > - { > - __format::_Str_sink<char> __buf; > - std::vformat_to(__buf.out(), __fmt, __args); > - auto __out = __buf.view(); > - if (std::fwrite(__out.data(), 1, __out.size(), __stream) != > __out.size()) > - __throw_system_error(EIO); > - } > + format_args __args); > > - inline void > - vprint_unicode(FILE* __stream, string_view __fmt, format_args __args) > - { > -#if !defined(_WIN32) || defined(__CYGWIN__) > - // For most targets we don't need to do anything special to write > - // Unicode to a terminal. > - std::vprint_nonunicode(__stream, __fmt, __args); > -#else > - __format::_Str_sink<char> __buf; > - std::vformat_to(__buf.out(), __fmt, __args); > - auto __out = __buf._M_span(); > + void > + vprint_unicode(FILE* __stream, string_view __fmt, format_args __args); > > - void* __open_terminal(FILE*); > - error_code __write_to_terminal(void*, span<char>); > - // If stream refers to a terminal, write a native Unicode string to > it. > - if (auto __term = __open_terminal(__stream)) > - { > - error_code __e; > - if (!std::fflush(__stream)) > - { > - __e = __write_to_terminal(__term, __out); > - if (!__e) > - return; > - if (__e == std::make_error_code(errc::illegal_byte_sequence)) > - return; > - } > - else > - __e = error_code(errno, generic_category()); > - _GLIBCXX_THROW_OR_ABORT(system_error(__e, "std::vprint_unicode")); > - } > + void > + vprint_unicode_buffered(FILE* __stream, string_view __fmt, > + format_args __args); > > - // Otherwise just write the string to the file. > - if (std::fwrite(__out.data(), 1, __out.size(), __stream) != > __out.size()) > - __throw_system_error(EIO); > -#endif > - } > + void > + vprint_unicode_buffered(string_view __fmt, format_args __args); > > - inline void > - vprint_unicode_buffered(FILE* __stream, string_view __fmt, format_args > __args) > - { > -#if !defined(_WIN32) || defined(__CYGWIN__) > - // For most targets we don't need to do anything special to write > - // Unicode to a terminal. Just use the nonunicode function. > - std::vprint_nonunicode_buffered(__stream, __fmt, __args); > -#else > - // For Windows the locking function formats everything first anyway, > - // so no formatting happens while a lock is taken. Just use that. > - std::vprint_unicode(__stream, __fmt, __args); > -#endif > - } > + void > + vprint_nonunicode_buffered(string_view __fmt, format_args __args); > > template<typename... _Args> > inline void > @@ -357,6 +115,7 @@ namespace __format > // and we know what that would call, so we can call that directly. > > auto __fmtargs = std::make_format_args(__args...); > + > #if defined(_WIN32) && !defined(__CYGWIN__) > if constexpr (__unicode::__literal_encoding_is_utf8()) > { > @@ -371,6 +130,15 @@ namespace __format > else > #endif > > +#ifdef _GLIBCXX_NO_INLINE_PRINT > + { > + string __fmtn; > + __fmtn.reserve(__fmt.get().size() + 1); > + __fmtn = __fmt.get(); > + __fmtn += '\n'; > + std::vprint_nonunicode(__stream, __fmtn, __fmtargs); > + } > +#else > I was wondering if we could somehow avoid allocation here, but it seem it will require introduction of two new functions, __vprintln_nonunicode and _buffered version. Anyway something to explore when we will cal C++23 stable. > // For non-Windows and for non-Unicode on Windows, we know that > print > // would call vprint_nonunicode or vprint_nonunicode_buffered with a > // newline appended to the format-string. Use a _File_sink that adds > @@ -387,6 +155,7 @@ namespace __format > string_view __s(__buf.view()); > __format::_File_sink(__stream, true).out() = __s; > } > +#endif > } > > template<typename... _Args> > diff --git a/libstdc++-v3/src/c++23/print.cc > b/libstdc++-v3/src/c++23/print.cc > index f34369950096..290e0b8dfa2d 100644 > --- a/libstdc++-v3/src/c++23/print.cc > +++ b/libstdc++-v3/src/c++23/print.cc > @@ -22,6 +22,13 @@ > // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see > // <http://www.gnu.org/licenses/>. > > +// We want to emit symbols for the inline functions in bits/print.h here. > +#define _GLIBCXX_NO_INLINE_PRINT 1 > +#include <ostream> > +#include <bits/ostream_print.h> > +#include <print> > +#include <bits/print.h> > + > #include <span> > #include <string> > #include <streambuf> > -- > 2.53.0 > >
