mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
The names of the vprint functions follow the convention from P3235R3. This takes advantage of the additional permission proposed by P3107R5 so that std::print can write directly to a FILE stream, rather than formatting to an intermediate std::string temporary and then writing that to the stream. The change is to write to a new _File_sink type instead of a _Str_sink that populates a std::string. There are three implementations of _File_sink. For non-Glibc targets that support POSIX flockfile and putc_unlocked, the stream will be locked and then formatted characters will be buffered on the stack (instead of allocating a std::string) and copied to the stream when the buffer fills up. For Glibc, _File_sink will lock the stream but then if the file is line-buffered or fully buffered, characters will be written directly into the file's output buffer. This avoids two levels of buffering and copying the characters from one to the other. For an unbuffered stream (like stderr) the _File_sink buffer will still be used, to avoid the overhead of lots of small writes to the stream. Because this version of _File_sink accesses the stream's buffer directly it relies on glibc-specific implementation details that are exposed in public headers. A fallback definition of _File_sink just wraps a _Str_sink so is equivalent to the original code, and is used when flockfile isn't available. Both forms of std::println (taking a FILE* and a std::ostream) can be implemented more efficiently by appending a newline to the format string, to avoid formatting twice. PR libstdc++/121790 libstdc++-v3/ChangeLog: * acinclude.m4 (GLIBCXX_CHECK_STDIO_LOCKING): New macro to check for std::print dependencies. * config.h.in: Regenerate. * configure: Regenerate. * configure.ac: Use GLIBCXX_CHECK_STDIO_LOCKING. * include/bits/formatfwd.h (enable_nonlocking_formatter_optimization): Define new variable template. * include/bits/version.def (print): Bump value. * include/bits/version.h: Regenerate. * include/std/format (enable_nonlocking_formatter_optimization): Define specializations for variable template. * include/std/ostream (print) [!_WIN32]: Do not use vprint_unicode at all. (println): Append newline to format string instead of formatting twice. * include/std/print (_File_sink): New class. (vprint_nonunicode_locking): New function. (vprint_unicode_locking): New function reusing previous code from vprint_unicode. (vprintf_unicode): Defer to vprint_nonunicode for Windows or to vprint_unicode_locking otherwise. (print): [!_WIN32]: Do no use vprint_unicode at all. Check enable_nonlocking_formatter_optimization and defer to either vprint_nonunicode_locking or vprint_nonunicode. (println): Use vprint_unicode or format directly to a _File_sink instead of formatting twice. * testsuite/27_io/print/1.cc: Updated and added new tests. * testsuite/std/format/formatter/nonlocking.cc: New tests. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com> Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
306 lines
8.6 KiB
C++
306 lines
8.6 KiB
C++
// Output streams -*- C++ -*-
|
|
|
|
// Copyright (C) 1997-2025 Free Software Foundation, Inc.
|
|
//
|
|
// 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/ostream
|
|
* This is a Standard C++ Library header.
|
|
*/
|
|
|
|
//
|
|
// ISO C++ 14882: 27.6.2 Output streams
|
|
//
|
|
|
|
#ifndef _GLIBCXX_OSTREAM
|
|
#define _GLIBCXX_OSTREAM 1
|
|
|
|
#ifdef _GLIBCXX_SYSHDR
|
|
#pragma GCC system_header
|
|
#endif
|
|
|
|
#include <bits/requires_hosted.h> // iostreams
|
|
|
|
#include <bits/ostream.h>
|
|
#if __cplusplus > 202002L
|
|
# include <format>
|
|
#endif
|
|
|
|
# define __glibcxx_want_print
|
|
#include <bits/version.h> // __glibcxx_syncbuf
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
// Standard basic_ostream manipulators
|
|
|
|
/**
|
|
* @brief Write a newline and flush the stream.
|
|
*
|
|
* This manipulator is often mistakenly used when a simple newline is
|
|
* desired, leading to poor buffering performance. See
|
|
* https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html#io.streambuf.buffering
|
|
* for more on this subject.
|
|
*/
|
|
template<typename _CharT, typename _Traits>
|
|
inline basic_ostream<_CharT, _Traits>&
|
|
endl(basic_ostream<_CharT, _Traits>& __os)
|
|
{ return flush(__os.put(__os.widen('\n'))); }
|
|
|
|
/**
|
|
* @brief Write a null character into the output sequence.
|
|
*
|
|
* <em>Null character</em> is @c CharT() by definition. For CharT
|
|
* of @c char, this correctly writes the ASCII @c NUL character
|
|
* string terminator.
|
|
*/
|
|
template<typename _CharT, typename _Traits>
|
|
inline basic_ostream<_CharT, _Traits>&
|
|
ends(basic_ostream<_CharT, _Traits>& __os)
|
|
{ return __os.put(_CharT()); }
|
|
|
|
/**
|
|
* @brief Flushes the output stream.
|
|
*
|
|
* This manipulator simply calls the stream's @c flush() member function.
|
|
*/
|
|
template<typename _CharT, typename _Traits>
|
|
inline basic_ostream<_CharT, _Traits>&
|
|
flush(basic_ostream<_CharT, _Traits>& __os)
|
|
{ return __os.flush(); }
|
|
|
|
#ifdef __glibcxx_syncbuf // C++ >= 20 && HOSTED && CXX11ABI
|
|
template<typename _CharT, typename _Traits>
|
|
class __syncbuf_base : public basic_streambuf<_CharT, _Traits>
|
|
{
|
|
public:
|
|
static bool*
|
|
_S_get(basic_streambuf<_CharT, _Traits>* __buf [[maybe_unused]]) noexcept
|
|
{
|
|
#if __cpp_rtti
|
|
if (auto __p = dynamic_cast<__syncbuf_base*>(__buf))
|
|
return &__p->_M_emit_on_sync;
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
protected:
|
|
__syncbuf_base(basic_streambuf<_CharT, _Traits>* __w = nullptr)
|
|
: _M_wrapped(__w)
|
|
{ }
|
|
|
|
basic_streambuf<_CharT, _Traits>* _M_wrapped = nullptr;
|
|
bool _M_emit_on_sync = false;
|
|
bool _M_needs_sync = false;
|
|
};
|
|
|
|
template<typename _CharT, typename _Traits>
|
|
inline basic_ostream<_CharT, _Traits>&
|
|
emit_on_flush(basic_ostream<_CharT, _Traits>& __os)
|
|
{
|
|
if (bool* __flag = __syncbuf_base<_CharT, _Traits>::_S_get(__os.rdbuf()))
|
|
*__flag = true;
|
|
return __os;
|
|
}
|
|
|
|
template<typename _CharT, typename _Traits>
|
|
inline basic_ostream<_CharT, _Traits>&
|
|
noemit_on_flush(basic_ostream<_CharT, _Traits>& __os)
|
|
{
|
|
if (bool* __flag = __syncbuf_base<_CharT, _Traits>::_S_get(__os.rdbuf()))
|
|
*__flag = false;
|
|
return __os;
|
|
}
|
|
|
|
template<typename _CharT, typename _Traits>
|
|
inline basic_ostream<_CharT, _Traits>&
|
|
flush_emit(basic_ostream<_CharT, _Traits>& __os)
|
|
{
|
|
struct _Restore
|
|
{
|
|
~_Restore() { *_M_flag = _M_prev; }
|
|
|
|
bool _M_prev = false;
|
|
bool* _M_flag = &_M_prev;
|
|
} __restore;
|
|
|
|
if (bool* __flag = __syncbuf_base<_CharT, _Traits>::_S_get(__os.rdbuf()))
|
|
{
|
|
__restore._M_prev = *__flag;
|
|
__restore._M_flag = __flag;
|
|
*__flag = true;
|
|
}
|
|
|
|
__os.flush();
|
|
return __os;
|
|
}
|
|
#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); }
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
template<typename... _Args>
|
|
inline void
|
|
print(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args)
|
|
{
|
|
auto __fmtargs = std::make_format_args(__args...);
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if constexpr (__unicode::__literal_encoding_is_utf8())
|
|
std::vprint_unicode(__os, __fmt.get(), __fmtargs);
|
|
else
|
|
#endif
|
|
std::vprint_nonunicode(__os, __fmt.get(), __fmtargs);
|
|
}
|
|
|
|
template<typename... _Args>
|
|
inline void
|
|
println(ostream& __os, format_string<_Args...> __fmt, _Args&&... __args)
|
|
{
|
|
auto __fmtargs = std::make_format_args(__args...);
|
|
std::string __fmtn;
|
|
__fmtn.reserve(__fmt.get().size() + 1);
|
|
__fmtn = __fmt.get();
|
|
__fmtn += '\n';
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if constexpr (__unicode::__literal_encoding_is_utf8())
|
|
std::vprint_unicode(__os, __fmtn, __fmtargs);
|
|
else
|
|
#endif
|
|
std::vprint_nonunicode(__os, __fmtn, __fmtargs);
|
|
}
|
|
|
|
// Defined for C++26, supported as an extension to C++23.
|
|
inline void println(ostream& __os)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if constexpr (__unicode::__literal_encoding_is_utf8())
|
|
std::vprint_unicode(__os, "\n", std::make_format_args());
|
|
else
|
|
#endif
|
|
__os.put('\n');
|
|
}
|
|
|
|
#endif // __cpp_lib_print
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace std
|
|
|
|
#include <bits/ostream.tcc>
|
|
|
|
#endif /* _GLIBCXX_OSTREAM */
|