]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Optionally define std::print functions non-inline [PR124410]
authorJonathan Wakely <jwakely@redhat.com>
Tue, 10 Mar 2026 21:19:09 +0000 (21:19 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 17 Apr 2026 12:43:49 +0000 (13:43 +0100)
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.

libstdc++-v3/doc/html/index.html
libstdc++-v3/doc/html/manual/using.html
libstdc++-v3/doc/html/manual/using_macros.html
libstdc++-v3/doc/xml/manual/using.xml
libstdc++-v3/include/Makefile.am
libstdc++-v3/include/Makefile.in
libstdc++-v3/include/bits/ostream_print.h [new file with mode: 0644]
libstdc++-v3/include/bits/print.h [new file with mode: 0644]
libstdc++-v3/include/std/ostream
libstdc++-v3/include/std/print
libstdc++-v3/src/c++23/print.cc

index 01d7bb787dcea910711f319692c1992c7506fef1..782f3bbc299c5be57045f95de15e41ef8265afe8 100644 (file)
@@ -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>
index fcd1b96de0d73ecbfda505b2e1532f32c1b0ba11..27f503ecaccc0a52ac17fa0a4de0d9472242a85a 100644 (file)
@@ -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,
index b1d05d99d760ddc6a17bb0a84fb3c3384db973b3..f1d8492fb9ef42b45fd6a326657f202a72e46c00 100644 (file)
        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
index 8adfecfe54bad40a4ed62cbc0a81cfb40b6a15a2..682669d91d4bec2b39bc7d7e88e4fb5c2bf0aa1d 100644 (file)
@@ -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,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>
index d0c97370700b72be1f35211287a22f3f579499d9..e9e46139da2fc3045dbb3aeef380a06e1ed9052d 100644 (file)
@@ -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 \
index a57fc00ce74220c5fa26474d764c4fe3b0b6583f..f3d2f58dbe429eccd8b94c49b4dcd39abe371358 100644 (file)
@@ -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 (file)
index 0000000..0adf16d
--- /dev/null
@@ -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 (file)
index 0000000..67a5a17
--- /dev/null
@@ -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
index e8dcfda2682f4a603a6aaf792998a4be806bf755..b99c0f92d34567716413b21555805b615ac12dec 100644 (file)
 #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
index 1b9e810419f0ef9afb41cf7319b0a7c2d0b5ee69..459bb9fb746205e6d611a62fed81b14ba0d2c111 100644 (file)
 
 #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>
index f34369950096191cecf4713e6b0b840225e730bb..290e0b8dfa2d721e8b9ef3de76bb9c3d9e426521 100644 (file)
 // 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>