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">&lt;stacktrace&gt;</code> header,
> +       the C++23 functions defined in the
> +       <code class="filename">&lt;print&gt;</code> header,
>          the Filesystem library extensions defined in the
>          <code class="filename">&lt;experimental/filesystem&gt;</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">&lt;stacktrace&gt;</filename> header,
> +       the C++23 functions defined in the
> +       <filename class="headerfile">&lt;print&gt;</filename> header,
>          the Filesystem library extensions defined in the
>          <filename
> class="headerfile">&lt;experimental/filesystem&gt;</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
>
>

Reply via email to