[gcc r16-8719] libstdc++: Optionally define std::print functions non-inline [PR124410]

Jonathan Wakely redi@gcc.gnu.org
Fri Apr 17 12:45:35 GMT 2026


https://gcc.gnu.org/g:fa4754797c2935e4e2be95f128e668d6499ec267

commit r16-8719-gfa4754797c2935e4e2be95f128e668d6499ec267
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Mar 10 21:19:09 2026 +0000

    libstdc++: Optionally define std::print functions non-inline [PR124410]
    
    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 8s to
    under 2s with trunk. For release branches with --enable-checking=release
    we should see even faster times. The object file size is also
    dramatically smaller, because there's just a single call to an extern
    function instead of instantiating the entire std::print and std::format
    implementation inline.
    
    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.

Diff:
---
 libstdc++-v3/doc/html/index.html               |   4 +-
 libstdc++-v3/doc/html/manual/using.html        |   2 +
 libstdc++-v3/doc/html/manual/using_macros.html |   9 +
 libstdc++-v3/doc/xml/manual/using.xml          |  15 ++
 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                 | 297 +++-------------------
 libstdc++-v3/src/c++23/print.cc                |   7 +
 11 files changed, 584 insertions(+), 366 deletions(-)

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..f1d8492fb9ef 100644
--- a/libstdc++-v3/doc/html/manual/using_macros.html
+++ b/libstdc++-v3/doc/html/manual/using_macros.html
@@ -147,4 +147,13 @@
 	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 implemented using inline functions.
+	This means that code using those functions will compile faster,
+	but <code class="option">-lstdc++exp</code> must be used when linking.
+	The non-inline definitions are compiled using
+	<code class="option">-fexec-charset=UTF-8</code> so might give incorrect results
+	if called from a source file that uses a non-Unicode encoding,
+	especially for format strings using non-ASCII fill characters.
       </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..682669d91d4b 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,19 @@ 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 implemented using inline functions.
+	This means that code using those functions will compile faster,
+	but <option>-lstdc++exp</option> must be used when linking.
+	The non-inline definitions are compiled using
+	<option>-fexec-charset=UTF-8</option> so might give incorrect results
+	if called from a source file that uses a non-Unicode encoding,
+	especially for format strings using non-ASCII fill characters.
+      </para>
+    </listitem></varlistentry>
     </variablelist>
 
   </section>
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index d0c97370700b..e9e46139da2f 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -244,7 +244,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 a57fc00ce742..f3d2f58dbe42 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -600,7 +600,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();
-
-	__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); }
-      }
-  }
+  void
+  vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args);
 
-  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 1b9e810419f0..459bb9fb7462 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;
-
-  // 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;
+  void
+  vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args);
 
-  public:
-    _File_sink(FILE* __f, bool __add_newline) noexcept
-    : _M_file(__f), _M_add_newline(__add_newline)
-    { }
+  void
+  vprint_nonunicode_buffered(FILE* __stream, string_view __fmt,
+			     format_args __args);
 
-    ~_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);
-    }
+  void
+  vprint_unicode(FILE* __stream, string_view __fmt, format_args __args);
 
-    auto out() { return _M_sink.out(); }
-  };
-#endif
-} // namespace __format
+  void
+  vprint_unicode_buffered(FILE* __stream, string_view __fmt,
+			  format_args __args);
 
-  inline void
-  vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args)
-  {
-    std::vformat_to(__format::_File_sink(__stream, false).out(), __fmt, __args);
-  }
+  void
+  vprint_unicode_buffered(string_view __fmt, format_args __args);
 
-  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);
-  }
-
-  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
-  }
-
-  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
       // 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>


More information about the Libstdc++-cvs mailing list