[llvm-branch-commits] [libcxx] [libc++][format] Implements P3107R5 in <print>. (PR #130500)
Mark de Wever via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Mar 14 09:12:07 PDT 2025
https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/130500
>From c132aa555a38efde9b04c2a3f435ba598778c28d Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 30 Mar 2024 17:35:56 +0100
Subject: [PATCH 1/2] [libc++][format] Implements P3107R5 in <print>.
The followup paper P3235R3 which is voted in as a DR changes the names
foo_locking to foo_buffered. These changes have been applied in this
patch.
Before
-------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------
printf 71.3 ns 71.3 ns 9525175
print_string 226 ns 226 ns 3105850
print_stack 232 ns 232 ns 3026498
print_direct 530 ns 530 ns 1318447
After
-------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------
printf 70.6 ns 70.6 ns 9789585
print_string 222 ns 222 ns 3147678
print_stack 227 ns 227 ns 3084767
print_direct 474 ns 474 ns 1472786
Note: The performance of libc++'s std::print is still extemely slow
compared to printf. Based on P3107R5 std::print should outperform
printf. The main culprit is the call to isatty, which is resolved
after implementing
LWG4044 Confusing requirements for std::print on POSIX platforms
Implements
- P3107R5 - Permit an efficient implementation of ``std::print``
Implements parts of
- P3235R3 std::print more types faster with less memory
Fixes: #105435
---
libcxx/docs/ReleaseNotes/21.rst | 1 +
libcxx/include/__format/buffer.h | 3 +
libcxx/include/print | 270 +++++++++++++++++-
libcxx/modules/std/print.inc | 1 +
.../test/libcxx/system_reserved_names.gen.py | 5 +
.../test/libcxx/transitive_includes/cxx03.csv | 5 +
.../test/libcxx/transitive_includes/cxx11.csv | 5 +
.../test/libcxx/transitive_includes/cxx14.csv | 5 +
.../test/libcxx/transitive_includes/cxx17.csv | 5 +
.../test/libcxx/transitive_includes/cxx23.csv | 5 +-
.../test/libcxx/transitive_includes/cxx26.csv | 4 +
11 files changed, 296 insertions(+), 13 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index e7cfa625a132c..a1f30b26c5a1d 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -40,6 +40,7 @@ Implemented Papers
- N4258: Cleaning-up noexcept in the Library (`Github <https://github.com/llvm/llvm-project/issues/99937>`__)
- P1361R2: Integration of chrono with text formatting (`Github <https://github.com/llvm/llvm-project/issues/100014>`__)
+- P3107R5 - Permit an efficient implementation of ``std::print`` (`Github <https://github.com/llvm/llvm-project/issues/105435>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h
index c88b7f3222010..d6e4ddc840e2d 100644
--- a/libcxx/include/__format/buffer.h
+++ b/libcxx/include/__format/buffer.h
@@ -12,6 +12,7 @@
#include <__algorithm/copy_n.h>
#include <__algorithm/fill_n.h>
+#include <__algorithm/for_each.h>
#include <__algorithm/max.h>
#include <__algorithm/min.h>
#include <__algorithm/ranges_copy.h>
@@ -34,11 +35,13 @@
#include <__memory/construct_at.h>
#include <__memory/destroy.h>
#include <__memory/uninitialized_algorithms.h>
+#include <__system_error/system_error.h>
#include <__type_traits/add_pointer.h>
#include <__type_traits/conditional.h>
#include <__utility/exception_guard.h>
#include <__utility/move.h>
#include <stdexcept>
+#include <stdio.h> // Uses the POSIX/Windows unlocked stream I/O
#include <string_view>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/print b/libcxx/include/print
index 1794d6014efcd..f6d03edfbd4bc 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -27,9 +27,11 @@ namespace std {
void vprint_unicode(string_view fmt, format_args args);
void vprint_unicode(FILE* stream, string_view fmt, format_args args);
+ void vprint_unicode_buffered(FILE* stream, string_view fmt, format_args args);
void vprint_nonunicode(string_view fmt, format_args args);
void vprint_nonunicode(FILE* stream, string_view fmt, format_args args);
+ void vprint_nonunicode_buffered(FILE* stream, string_view fmt, format_args args);
}
*/
@@ -41,6 +43,7 @@ namespace std {
# include <__config>
# include <__system_error/throw_system_error.h>
# include <__utility/forward.h>
+# include <__utility/move.h>
# include <cerrno>
# include <cstdio>
# include <format>
@@ -52,6 +55,9 @@ namespace std {
# pragma GCC system_header
# endif
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
# ifdef _LIBCPP_WIN32API
@@ -213,6 +219,122 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
# endif
}
+_LIBCPP_HIDE_FROM_ABI inline void __flockfile(FILE* __stream) {
+# if defined(_LIBCPP_WIN32API)
+ ::_lock_file(__stream);
+# elif __has_include(<unistd.h>)
+ ::flockfile(__stream);
+# else
+# error "Provide a way to do unlocked stream I/O operations"
+# endif
+}
+_LIBCPP_HIDE_FROM_ABI inline void __funlockfile(FILE* __stream) {
+# if defined(_LIBCPP_WIN32API)
+ ::_unlock_file(__stream);
+# elif __has_include(<unistd.h>)
+ ::funlockfile(__stream);
+# else
+# error "Provide a way to do unlocked stream I/O operations"
+# endif
+}
+
+_LIBCPP_HIDE_FROM_ABI inline int __fflush_unlocked(FILE* __stream) {
+# if defined(_LIBCPP_WIN32API)
+ return ::_fflush_nolock(__stream);
+# elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
+ // There is no fflush_unlocked on these systems.
+ // This funcion is not part of POSIX.
+ return ::fflush(__stream);
+# elif __has_include(<unistd.h>)
+ return ::fflush_unlocked(__stream);
+# else
+# error "Provide a way to do unlocked stream I/O operations"
+# endif
+}
+
+// Note for our use-case __size is always 1;
+_LIBCPP_HIDE_FROM_ABI inline size_t
+__fwrite_unlocked(const void* __buffer, [[maybe_unused]] size_t __size, size_t __n, FILE* __stream) {
+# if defined(_LIBCPP_WIN32API)
+ return ::_fwrite_nolock(__buffer, __size, __n, __stream);
+# elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
+ // There is no fwrite_unlocked on these systems.
+ // This funcion is not part of POSIX.
+ auto __b = static_cast<const char*>(__buffer);
+ for (size_t __i = 0; __i < __n; ++__i, ++__b)
+ // Unqualified since putc_unlocked is a macro on AIX.
+ if (putc_unlocked(*__b, __stream) == EOF)
+ return __i;
+ return __n;
+# elif __has_include(<unistd.h>)
+ return ::fwrite_unlocked(__buffer, __size, __n, __stream);
+# else
+# error "Provide a way to do unlocked stream I/O operations"
+# endif
+}
+
+// This "buffer" is not a typical buffer but an adaptor for FILE*
+//
+// This adaptor locks the file stream, allowing it to use unlocked I/O.
+// This is used by the *_buffered functions in <print>. The print functions have
+// no wchar_t support so char is hard-coded. Since the underlaying I/O functions
+// encode narrow or wide in their name this avoids some `if constexpr` branches.
+//
+// The underlying functions for unlocked I/O are not in the C Standard, and
+// their names differ between POSIX and Windows, therefore the functions are
+// wrapped in this class.
+class __file_stream_buffer : public __format::__output_buffer<char> {
+public:
+ using value_type = char;
+
+ __file_stream_buffer(const __file_stream_buffer&) = delete;
+ __file_stream_buffer operator=(const __file_stream_buffer&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer(FILE* __stream)
+ : __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr}, __stream_(__stream) {
+ __print::__flockfile(__stream_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer() { __print::__funlockfile(__stream_); }
+
+ // In order to ensure all data is written this function needs to be called.
+ //
+ // The class wraps C based APIs that never throw. However the Standard
+ // requires exceptions to be throw when a write operation fails. Therefore
+ // this function should be called before the class is destroyed.
+ _LIBCPP_HIDE_FROM_ABI void __write_internal_buffer() && { __write_buffer(); }
+
+private:
+ FILE* __stream_;
+
+ // This class uses a fixed size buffer and appends the elements in
+ // __buffer_size chunks. An alternative would be to use an allocating buffer
+ // and append the output in a single write operation. Benchmarking showed no
+ // performance difference.
+ static constexpr size_t __buffer_size = 256;
+ char __small_buffer_[__buffer_size];
+
+ _LIBCPP_HIDE_FROM_ABI void __write_buffer() {
+ size_t __n = this->__size();
+ size_t __size = __print::__fwrite_unlocked(__small_buffer_, 1, __n, __stream_);
+ if (__size < __n) {
+ if (std::feof(__stream_))
+ std::__throw_system_error(EIO, "EOF while writing the formatted output");
+ std::__throw_system_error(std::ferror(__stream_), "failed to write formatted output");
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void __prepare_write() {
+ __write_buffer();
+ this->__buffer_flushed();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static void
+ __prepare_write(__output_buffer<char>& __buffer, [[maybe_unused]] size_t __size_hint) {
+ static_cast<__file_stream_buffer&>(__buffer).__prepare_write();
+ }
+};
+
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
_LIBCPP_HIDE_FROM_ABI inline void
__vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
@@ -229,6 +351,26 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
}
}
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered(
+ __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
+ std::__format::__vformat_to(basic_format_parse_context{__fmt, __args.__size()},
+ std::__format_context_create(__buffer.__make_output_iterator(), __args));
+ if (__write_nl)
+ __buffer.push_back('\n');
+}
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void
+__vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
+ _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+ __print::__file_stream_buffer __buffer(__stream);
+
+ __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
+
+ std::move(__buffer).__write_internal_buffer();
+}
+
# if _LIBCPP_HAS_UNICODE
// Note these helper functions are mainly used to aid testing.
@@ -246,10 +388,27 @@ __vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bo
__print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
}
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix(
+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+ _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+ __print::__file_stream_buffer __buffer(__stream);
+
+ // TODO PRINT Should flush errors throw too?
+ if (__is_terminal)
+ __print::__fflush_unlocked(__stream);
+
+ __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
+
+ std::move(__buffer).__write_internal_buffer();
+}
# if _LIBCPP_HAS_WIDE_CHARACTERS
+
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
_LIBCPP_HIDE_FROM_ABI inline void
__vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+ _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+
if (!__is_terminal)
return __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
@@ -284,6 +443,49 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
"__write_to_windows_console is not available.");
# endif
}
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows(
+ FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+ _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+
+ if (!__is_terminal)
+ return __print::__vprint_nonunicode_buffered(__stream, __fmt, __args, __write_nl);
+
+ [[maybe_unused]] __print::__file_stream_buffer __b(__stream);
+
+ // TODO PRINT Should flush errors throw too?
+ __print::__fflush_unlocked(__stream);
+
+ string __str = std::vformat(__fmt, __args);
+ // UTF-16 uses the same number or less code units than UTF-8.
+ // However the size of the code unit is 16 bits instead of 8 bits.
+ //
+ // The buffer uses the worst-case estimate and should never resize.
+ // However when the string is large this could lead to OOM. Using a
+ // smaller size might work, but since the buffer uses a grow factor
+ // the final size might be larger when the estimate is wrong.
+ //
+ // TODO PRINT profile and improve the speed of this code.
+ __format::__retarget_buffer<wchar_t> __buffer{__str.size()};
+ __unicode::__transcode(__str.begin(), __str.end(), __buffer.__make_output_iterator());
+ if (__write_nl)
+ __buffer.push_back(L'\n');
+
+ [[maybe_unused]] wstring_view __view = __buffer.__view();
+
+ // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
+ // the behavior in the test. This is not part of the public API.
+# ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
+ _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
+# elif defined(_LIBCPP_WIN32API)
+ std::__write_to_windows_console(__stream, __view);
+# else
+ std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
+ "__write_to_windows_console is not available.");
+# endif
+}
+
# endif // _LIBCPP_HAS_WIDE_CHARACTERS
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -324,6 +526,23 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
# endif
}
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered(
+ [[maybe_unused]] FILE* __stream,
+ [[maybe_unused]] string_view __fmt,
+ [[maybe_unused]] format_args __args,
+ [[maybe_unused]] bool __write_nl) {
+ _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
+
+# ifndef _LIBCPP_WIN32API
+ __print::__vprint_unicode_buffered_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+# elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
+ __print::__vprint_unicode_buffered_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+# else
+# error "Windows builds with wchar_t disabled are not supported."
+# endif
+}
+
# endif // _LIBCPP_HAS_UNICODE
} // namespace __print
@@ -331,13 +550,23 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI void print(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
# if _LIBCPP_HAS_UNICODE
- if constexpr (__print::__use_unicode_execution_charset)
- __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
- else
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
# else // _LIBCPP_HAS_UNICODE
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ constexpr bool __use_unicode = false;
# endif // _LIBCPP_HAS_UNICODE
+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
+
+ if constexpr (__use_unicode) {
+ if constexpr (__locksafe)
+ __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ else
+ __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ } else {
+ if constexpr (__locksafe)
+ __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ else
+ __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ }
}
template <class... _Args>
@@ -348,16 +577,26 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
# if _LIBCPP_HAS_UNICODE
+ constexpr bool __use_unicode = __print::__use_unicode_execution_charset;
+# else // _LIBCPP_HAS_UNICODE
+ constexpr bool __use_unicode = false;
+# endif // _LIBCPP_HAS_UNICODE
+ constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
+
// Note the wording in the Standard is inefficient. The output of
// std::format is a std::string which is then copied. This solution
// just appends a newline at the end of the output.
- if constexpr (__print::__use_unicode_execution_charset)
- __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
- else
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
-# else // _LIBCPP_HAS_UNICODE
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
-# endif // _LIBCPP_HAS_UNICODE
+ if constexpr (__use_unicode) {
+ if constexpr (__locksafe)
+ __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
+ else
+ __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
+ } else {
+ if constexpr (__locksafe)
+ __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
+ else
+ __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
+ }
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -381,6 +620,11 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __f
__print::__vprint_unicode(__stream, __fmt, __args, false);
}
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered(FILE* __stream, string_view __fmt, format_args __args) {
+ __print::__vprint_unicode_buffered(__stream, __fmt, __args, false);
+}
+
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(string_view __fmt, format_args __args) {
std::vprint_unicode(stdout, __fmt, __args);
@@ -402,6 +646,8 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(string_view __fmt, format_ar
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
#endif // _LIBCPP_PRINT
diff --git a/libcxx/modules/std/print.inc b/libcxx/modules/std/print.inc
index 5354025ca8bd8..9c6d8b20a22d8 100644
--- a/libcxx/modules/std/print.inc
+++ b/libcxx/modules/std/print.inc
@@ -16,6 +16,7 @@ export namespace std {
using std::vprint_nonunicode;
# if _LIBCPP_HAS_UNICODE
using std::vprint_unicode;
+ using std::vprint_unicode_buffered;
# endif // _LIBCPP_HAS_UNICODE
#endif // _LIBCPP_STD_VER >= 23
} // namespace std
diff --git a/libcxx/test/libcxx/system_reserved_names.gen.py b/libcxx/test/libcxx/system_reserved_names.gen.py
index f01126249c881..304c803b76c3d 100644
--- a/libcxx/test/libcxx/system_reserved_names.gen.py
+++ b/libcxx/test/libcxx/system_reserved_names.gen.py
@@ -119,6 +119,11 @@
#define __acquire SYSTEM_RESERVED_NAME
#define __release SYSTEM_RESERVED_NAME
+// Android and FreeBSD use this for __attribute__((__unused__))
+#if !defined(__FreeBSD__) && !defined(__ANDROID__)
+#define __unused SYSTEM_RESERVED_NAME
+#endif
+
// These names are not reserved, so the user can macro-define them.
// These are intended to find improperly _Uglified template parameters.
#define A SYSTEM_RESERVED_NAME
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index ec5db90597d92..7d52c9fe13594 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2024,6 +2028,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index ec5db90597d92..7d52c9fe13594 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2024,6 +2028,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 95024df0590b8..951a785cf077b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2064,6 +2068,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index a3518f7f62ecb..7742c23b796f7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
atomic type_traits
atomic version
barrier atomic
+barrier cctype
barrier climits
barrier cmath
barrier compare
barrier concepts
barrier cstddef
barrier cstdint
+barrier cstdio
barrier cstdlib
barrier cstring
barrier ctime
+barrier cwchar
+barrier cwctype
barrier exception
barrier initializer_list
barrier iosfwd
@@ -2077,6 +2081,7 @@ stdexcept new
stdexcept type_traits
stdexcept typeinfo
stdexcept version
+stop_token cstddef
stop_token iosfwd
stop_token version
streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 17972b8453743..167c79130bfbf 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -556,7 +556,6 @@ istream ios
istream iosfwd
istream limits
istream locale
-
istream ratio
istream stdexcept
istream streambuf
@@ -765,6 +764,7 @@ queue deque
queue initializer_list
queue iosfwd
queue limits
+queue optional
queue stdexcept
queue string
queue string_view
@@ -831,6 +831,7 @@ regex deque
regex initializer_list
regex iosfwd
regex limits
+regex optional
regex stdexcept
regex string
regex string_view
@@ -1075,6 +1076,7 @@ thread iosfwd
thread istream
thread limits
thread locale
+thread optional
thread ratio
thread sstream
thread stdexcept
@@ -1146,6 +1148,7 @@ vector cwctype
vector initializer_list
vector iosfwd
vector limits
+vector optional
vector stdexcept
vector string
vector string_view
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 00ab78e61a457..2bc61974a2ad8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -763,6 +763,7 @@ queue deque
queue initializer_list
queue iosfwd
queue limits
+queue optional
queue stdexcept
queue string
queue string_view
@@ -829,6 +830,7 @@ regex deque
regex initializer_list
regex iosfwd
regex limits
+regex optional
regex stdexcept
regex string
regex string_view
@@ -1073,6 +1075,7 @@ thread iosfwd
thread istream
thread limits
thread locale
+thread optional
thread ratio
thread sstream
thread stdexcept
@@ -1144,6 +1147,7 @@ vector cwctype
vector initializer_list
vector iosfwd
vector limits
+vector optional
vector stdexcept
vector string
vector string_view
>From 4fcce8dc4c58ccc092087d89c4ee73c326165373 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Thu, 13 Mar 2025 18:44:10 +0100
Subject: [PATCH 2/2] Cleanups and improvements.
---
libcxx/include/__format/buffer.h | 3 -
libcxx/include/__ostream/print.h | 4 +-
libcxx/include/print | 257 +++++++-----------
libcxx/modules/std/print.inc | 1 +
.../print.fun/vprint_unicode_posix.pass.cpp | 13 +-
.../print.fun/vprint_unicode_windows.pass.cpp | 49 ++--
.../test/libcxx/transitive_includes/cxx23.csv | 4 -
.../test/libcxx/transitive_includes/cxx26.csv | 4 -
.../vprint_nonunicode_buffered.file.pass.cpp | 149 ++++++++++
.../vprint_unicode_buffered.file.pass.cpp | 156 +++++++++++
10 files changed, 454 insertions(+), 186 deletions(-)
create mode 100644 libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp
create mode 100644 libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp
diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h
index d6e4ddc840e2d..c88b7f3222010 100644
--- a/libcxx/include/__format/buffer.h
+++ b/libcxx/include/__format/buffer.h
@@ -12,7 +12,6 @@
#include <__algorithm/copy_n.h>
#include <__algorithm/fill_n.h>
-#include <__algorithm/for_each.h>
#include <__algorithm/max.h>
#include <__algorithm/min.h>
#include <__algorithm/ranges_copy.h>
@@ -35,13 +34,11 @@
#include <__memory/construct_at.h>
#include <__memory/destroy.h>
#include <__memory/uninitialized_algorithms.h>
-#include <__system_error/system_error.h>
#include <__type_traits/add_pointer.h>
#include <__type_traits/conditional.h>
#include <__utility/exception_guard.h>
#include <__utility/move.h>
#include <stdexcept>
-#include <stdio.h> // Uses the POSIX/Windows unlocked stream I/O
#include <string_view>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index a4d7506cffc48..b5ce81ea5a10a 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -111,9 +111,9 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
ostream::sentry __s(__os);
if (__s) {
# ifndef _LIBCPP_WIN32API
- __print::__vprint_unicode_posix(__file, __fmt, __args, __write_nl, true);
+ __print::__vprint_unicode_posix<__print::__lock_policy::__stdio>(__file, __fmt, __args, __write_nl, true);
# elif _LIBCPP_HAS_WIDE_CHARACTERS
- __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl, true);
+ __print::__vprint_unicode_windows<__print::__lock_policy::__stdio>(__file, __fmt, __args, __write_nl, true);
# else
# error "Windows builds with wchar_t disabled are not supported."
# endif
diff --git a/libcxx/include/print b/libcxx/include/print
index f6d03edfbd4bc..3372b08bbcad1 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -258,11 +258,11 @@ __fwrite_unlocked(const void* __buffer, [[maybe_unused]] size_t __size, size_t _
# if defined(_LIBCPP_WIN32API)
return ::_fwrite_nolock(__buffer, __size, __n, __stream);
# elif defined(__PICOLIBC__) || defined(_AIX) || defined(__ANDROID__)
- // There is no fwrite_unlocked on these systems.
- // This funcion is not part of POSIX.
+ // The function fwrite_unlocked is not part of POSIX and not available on
+ // these systems.
auto __b = static_cast<const char*>(__buffer);
for (size_t __i = 0; __i < __n; ++__i, ++__b)
- // Unqualified since putc_unlocked is a macro on AIX.
+ // Unqualified since putc_unlocked is a macro on AIX.
if (putc_unlocked(*__b, __stream) == EOF)
return __i;
return __n;
@@ -273,17 +273,37 @@ __fwrite_unlocked(const void* __buffer, [[maybe_unused]] size_t __size, size_t _
# endif
}
-// This "buffer" is not a typical buffer but an adaptor for FILE*
-//
-// This adaptor locks the file stream, allowing it to use unlocked I/O.
-// This is used by the *_buffered functions in <print>. The print functions have
-// no wchar_t support so char is hard-coded. Since the underlaying I/O functions
-// encode narrow or wide in their name this avoids some `if constexpr` branches.
+enum class __lock_policy {
+ // The locking is done manually, which allows calling the *_unlocked functions.
+ //
+ // These are used in the *_buffered overloads.
+ __manual,
+ // The locking is done inside the stdio library.
+ __stdio,
+
+};
+
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI int __fflush(FILE* __stream) {
+ if constexpr (__policy == __lock_policy::__manual)
+ return __print ::__fflush_unlocked(__stream);
+ else if constexpr (__policy == __lock_policy::__stdio)
+ return std::fflush(__stream);
+ else
+# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+ sizeof(__lock_policy) == 0
+# else
+ static_assert(false, "Unsupported policy");
+# endif
+}
+
+// This "buffer" is not a typical buffer but an buffered adaptor for FILE*
//
-// The underlying functions for unlocked I/O are not in the C Standard, and
-// their names differ between POSIX and Windows, therefore the functions are
-// wrapped in this class.
-class __file_stream_buffer : public __format::__output_buffer<char> {
+// Based on the __lock_policy it will leave the locking to stdio or manually
+// locks the stream and then uses unlocked stdio functions. This policy makes
+// the print functions "foo" and "foo_buffered" to be templated.
+template <__lock_policy __policy>
+class _LIBCPP_TEMPLATE_VIS __file_stream_buffer : public __format::__output_buffer<char> {
public:
using value_type = char;
@@ -292,10 +312,26 @@ public:
_LIBCPP_HIDE_FROM_ABI explicit __file_stream_buffer(FILE* __stream)
: __output_buffer<char>{__small_buffer_, __buffer_size, __prepare_write, nullptr}, __stream_(__stream) {
- __print::__flockfile(__stream_);
+ if constexpr (__policy == __lock_policy::__manual)
+ __print::__flockfile(__stream_);
+ else if constexpr (__policy != __lock_policy::__stdio)
+# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+ sizeof(__lock_policy) == 0
+# else
+ static_assert(false, "Unsupported policy");
+# endif
}
- _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer() { __print::__funlockfile(__stream_); }
+ _LIBCPP_HIDE_FROM_ABI ~__file_stream_buffer() {
+ if constexpr (__policy == __lock_policy::__manual)
+ __print::__funlockfile(__stream_);
+ else if constexpr (__policy != __lock_policy::__stdio)
+# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+ sizeof(__lock_policy) == 0
+# else
+ static_assert(false, "Unsupported policy");
+# endif
+ }
// In order to ensure all data is written this function needs to be called.
//
@@ -316,7 +352,19 @@ private:
_LIBCPP_HIDE_FROM_ABI void __write_buffer() {
size_t __n = this->__size();
- size_t __size = __print::__fwrite_unlocked(__small_buffer_, 1, __n, __stream_);
+ size_t __size = [&] {
+ if constexpr (__policy == __lock_policy::__manual)
+ return __print::__fwrite_unlocked(__small_buffer_, 1, __n, __stream_);
+ else if constexpr (__policy == __lock_policy::__stdio)
+ return std::fwrite(__small_buffer_, 1, __n, __stream_);
+ else
+# if defined(_LIBCPP_APPLE_CLANG_VER) && _LIBCPP_APPLE_CLANG_VER < 1600
+ sizeof(__lock_policy) == 0
+# else
+ static_assert(false, "Unsupported policy");
+# endif
+ }();
+
if (__size < __n) {
if (std::feof(__stream_))
std::__throw_system_error(EIO, "EOF while writing the formatted output");
@@ -331,42 +379,19 @@ private:
_LIBCPP_HIDE_FROM_ABI static void
__prepare_write(__output_buffer<char>& __buffer, [[maybe_unused]] size_t __size_hint) {
- static_cast<__file_stream_buffer&>(__buffer).__prepare_write();
+ static_cast<__file_stream_buffer<__policy>&>(__buffer).__prepare_write();
}
};
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
-__vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
_LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
- string __str = std::vformat(__fmt, __args);
- if (__write_nl)
- __str.push_back('\n');
-
- size_t __size = fwrite(__str.data(), 1, __str.size(), __stream);
- if (__size < __str.size()) {
- if (std::feof(__stream))
- std::__throw_system_error(EIO, "EOF while writing the formatted output");
- std::__throw_system_error(std::ferror(__stream), "failed to write formatted output");
- }
-}
+ __print::__file_stream_buffer<__policy> __buffer(__stream);
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_nonunicode_buffered(
- __print::__file_stream_buffer& __buffer, string_view __fmt, format_args __args, bool __write_nl) {
std::__format::__vformat_to(basic_format_parse_context{__fmt, __args.__size()},
std::__format_context_create(__buffer.__make_output_iterator(), __args));
if (__write_nl)
__buffer.push_back('\n');
-}
-
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
-__vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
- _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
- __print::__file_stream_buffer __buffer(__stream);
-
- __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
std::move(__buffer).__write_internal_buffer();
}
@@ -378,84 +403,33 @@ __vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __ar
// terminal when the output is redirected. Typically during testing the
// output is redirected to be able to capture it. This makes it hard to
// test this code path.
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
-__vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
- // TODO PRINT Should flush errors throw too?
- if (__is_terminal)
- std::fflush(__stream);
-
- __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
-}
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_posix(
- FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void
+__vprint_unicode_posix(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
_LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
- __print::__file_stream_buffer __buffer(__stream);
// TODO PRINT Should flush errors throw too?
if (__is_terminal)
- __print::__fflush_unlocked(__stream);
-
- __print::__vprint_nonunicode_buffered(__buffer, __fmt, __args, __write_nl);
+ __print::__fflush<__policy>(__stream);
- std::move(__buffer).__write_internal_buffer();
+ __print::__vprint_nonunicode<__policy>(__stream, __fmt, __args, __write_nl);
}
+
# if _LIBCPP_HAS_WIDE_CHARACTERS
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void
__vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
_LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
if (!__is_terminal)
- return __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
-
- // TODO PRINT Should flush errors throw too?
- std::fflush(__stream);
-
- string __str = std::vformat(__fmt, __args);
- // UTF-16 uses the same number or less code units than UTF-8.
- // However the size of the code unit is 16 bits instead of 8 bits.
- //
- // The buffer uses the worst-case estimate and should never resize.
- // However when the string is large this could lead to OOM. Using a
- // smaller size might work, but since the buffer uses a grow factor
- // the final size might be larger when the estimate is wrong.
- //
- // TODO PRINT profile and improve the speed of this code.
- __format::__retarget_buffer<wchar_t> __buffer{__str.size()};
- __unicode::__transcode(__str.begin(), __str.end(), __buffer.__make_output_iterator());
- if (__write_nl)
- __buffer.push_back(L'\n');
-
- [[maybe_unused]] wstring_view __view = __buffer.__view();
-
- // The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
- // the behavior in the test. This is not part of the public API.
-# ifdef _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION
- _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION(__stream, __view);
-# elif defined(_LIBCPP_WIN32API)
- std::__write_to_windows_console(__stream, __view);
-# else
- std::__throw_runtime_error("No defintion of _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION and "
- "__write_to_windows_console is not available.");
-# endif
-}
-
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows(
- FILE* __stream, string_view __fmt, format_args __args, bool __write_nl, bool __is_terminal) {
- _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
-
- if (!__is_terminal)
- return __print::__vprint_nonunicode_buffered(__stream, __fmt, __args, __write_nl);
+ return __print::__vprint_nonunicode<__policy>(__stream, __fmt, __args, __write_nl);
- [[maybe_unused]] __print::__file_stream_buffer __b(__stream);
+ [[maybe_unused]] __print::__file_stream_buffer<__policy> __b(__stream);
// TODO PRINT Should flush errors throw too?
- __print::__fflush_unlocked(__stream);
+ __print::__fflush<__policy>(__stream);
string __str = std::vformat(__fmt, __args);
// UTF-16 uses the same number or less code units than UTF-8.
@@ -488,8 +462,8 @@ _LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered_windows(
# endif // _LIBCPP_HAS_WIDE_CHARACTERS
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void
+template <__lock_policy __policy>
+_LIBCPP_HIDE_FROM_ABI void
__vprint_unicode([[maybe_unused]] FILE* __stream,
[[maybe_unused]] string_view __fmt,
[[maybe_unused]] format_args __args,
@@ -518,31 +492,13 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
// Windows there is a different API. This API requires transcoding.
# ifndef _LIBCPP_WIN32API
- __print::__vprint_unicode_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+ __print::__vprint_unicode_posix<__policy>(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
# elif _LIBCPP_HAS_WIDE_CHARACTERS
- __print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
+ __print::__vprint_unicode_windows<__policy>(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
# else
# error "Windows builds with wchar_t disabled are not supported."
# endif
}
-
-template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void __vprint_unicode_buffered(
- [[maybe_unused]] FILE* __stream,
- [[maybe_unused]] string_view __fmt,
- [[maybe_unused]] format_args __args,
- [[maybe_unused]] bool __write_nl) {
- _LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
-
-# ifndef _LIBCPP_WIN32API
- __print::__vprint_unicode_buffered_posix(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
-# elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
- __print::__vprint_unicode_buffered_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
-# else
-# error "Windows builds with wchar_t disabled are not supported."
-# endif
-}
-
# endif // _LIBCPP_HAS_UNICODE
} // namespace __print
@@ -556,17 +512,13 @@ _LIBCPP_HIDE_FROM_ABI void print(FILE* __stream, format_string<_Args...> __fmt,
# endif // _LIBCPP_HAS_UNICODE
constexpr bool __locksafe = (enable_nonlocking_formatter_optimization<remove_cvref_t<_Args>> && ...);
- if constexpr (__use_unicode) {
- if constexpr (__locksafe)
- __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
- else
- __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
- } else {
- if constexpr (__locksafe)
- __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), false);
- else
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
- }
+ using enum __print::__lock_policy;
+ if constexpr (__use_unicode)
+ __print::__vprint_unicode<__locksafe ? __manual : __stdio>(
+ __stream, __fmt.get(), std::make_format_args(__args...), false);
+ else
+ __print::__vprint_nonunicode<__locksafe ? __manual : __stdio>(
+ __stream, __fmt.get(), std::make_format_args(__args...), false);
}
template <class... _Args>
@@ -586,17 +538,13 @@ _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt
// Note the wording in the Standard is inefficient. The output of
// std::format is a std::string which is then copied. This solution
// just appends a newline at the end of the output.
- if constexpr (__use_unicode) {
- if constexpr (__locksafe)
- __print::__vprint_unicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
- else
- __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
- } else {
- if constexpr (__locksafe)
- __print::__vprint_nonunicode_buffered(__stream, __fmt.get(), std::make_format_args(__args...), true);
- else
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
- }
+ using enum __print::__lock_policy;
+ if constexpr (__use_unicode)
+ __print::__vprint_unicode<__locksafe ? __manual : __stdio>(
+ __stream, __fmt.get(), std::make_format_args(__args...), true);
+ else
+ __print::__vprint_nonunicode<__locksafe ? __manual : __stdio>(
+ __stream, __fmt.get(), std::make_format_args(__args...), true);
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -617,12 +565,12 @@ _LIBCPP_HIDE_FROM_ABI void println(format_string<_Args...> __fmt, _Args&&... __a
# if _LIBCPP_HAS_UNICODE
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __fmt, format_args __args) {
- __print::__vprint_unicode(__stream, __fmt, __args, false);
+ __print::__vprint_unicode<__print::__lock_policy::__stdio>(__stream, __fmt, __args, false);
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode_buffered(FILE* __stream, string_view __fmt, format_args __args) {
- __print::__vprint_unicode_buffered(__stream, __fmt, __args, false);
+ __print::__vprint_unicode<__print::__lock_policy::__manual>(__stream, __fmt, __args, false);
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -634,7 +582,12 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(string_view __fmt, format_args
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
_LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args) {
- __print::__vprint_nonunicode(__stream, __fmt, __args, false);
+ __print::__vprint_nonunicode< __print::__lock_policy::__stdio >(__stream, __fmt, __args, false);
+}
+
+template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
+_LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode_buffered(FILE* __stream, string_view __fmt, format_args __args) {
+ __print::__vprint_nonunicode<__print::__lock_policy::__manual>(__stream, __fmt, __args, false);
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/modules/std/print.inc b/libcxx/modules/std/print.inc
index 9c6d8b20a22d8..123bce409a06a 100644
--- a/libcxx/modules/std/print.inc
+++ b/libcxx/modules/std/print.inc
@@ -14,6 +14,7 @@ export namespace std {
using std::println;
using std::vprint_nonunicode;
+ using ::std::vprint_nonunicode_buffered;
# if _LIBCPP_HAS_UNICODE
using std::vprint_unicode;
using std::vprint_unicode_buffered;
diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp
index b89d02ba99425..db92811f803fa 100644
--- a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp
+++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_posix.pass.cpp
@@ -21,6 +21,7 @@
// <print>
// Tests the implementation of
+// template <__print::__lock_policy __policy>
// void __print::__vprint_unicode_posix(FILE* __stream, string_view __fmt,
// format_args __args, bool __write_nl,
// bool __is_terminal);
@@ -39,7 +40,8 @@
#include "test_macros.h"
-int main(int, char**) {
+template <std::__print::__lock_policy policy>
+static void test() {
std::array<char, 100> buffer;
std::ranges::fill(buffer, '*');
@@ -55,7 +57,7 @@ int main(int, char**) {
#endif
// Test writing to a "non-terminal" stream does not flush.
- std::__print::__vprint_unicode_posix(file, " world", std::make_format_args(), false, false);
+ std::__print::__vprint_unicode_posix<policy>(file, " world", std::make_format_args(), false, false);
assert(std::ftell(file) == 11);
#if defined(TEST_HAS_GLIBC) && \
!(__has_feature(address_sanitizer) || __has_feature(thread_sanitizer) || __has_feature(memory_sanitizer))
@@ -63,7 +65,7 @@ int main(int, char**) {
#endif
// Test writing to a "terminal" stream flushes before writing.
- std::__print::__vprint_unicode_posix(file, "!", std::make_format_args(), false, true);
+ std::__print::__vprint_unicode_posix<policy>(file, "!", std::make_format_args(), false, true);
assert(std::ftell(file) == 12);
assert(std::string_view(buffer.data(), buffer.data() + 11) == "Hello world");
#if defined(TEST_HAS_GLIBC)
@@ -74,6 +76,11 @@ int main(int, char**) {
// Test everything is written when closing the stream.
std::fclose(file);
assert(std::string_view(buffer.data(), buffer.data() + 12) == "Hello world!");
+}
+
+int main(int, char**) {
+ test<std::__print::__lock_policy::__manual>();
+ test<std::__print::__lock_policy::__stdio>();
return 0;
}
diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp
index bcd1d05a3aeeb..8028d7c04b85b 100644
--- a/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp
+++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/vprint_unicode_windows.pass.cpp
@@ -20,6 +20,7 @@
// <print>
// Tests the implementation of
+// template <__print::__lock_policy __policy>
// void __print::__vprint_unicode_windows(FILE* __stream, string_view __fmt,
// format_args __args, bool __write_nl,
// bool __is_terminal);
@@ -47,8 +48,8 @@ TEST_GCC_DIAGNOSTIC_IGNORED("-Wuse-after-free")
#define SV(S) MAKE_STRING_VIEW(wchar_t, S)
-bool calling = false;
-std::wstring_view expected = L" world";
+bool calling = true;
+std::wstring_view expected;
void write_to_console(FILE*, std::wstring_view data) {
assert(calling);
@@ -58,29 +59,35 @@ void write_to_console(FILE*, std::wstring_view data) {
scoped_test_env env;
std::string filename = env.create_file("output.txt");
+template <std::__print::__lock_policy policy>
static void test_basics() {
FILE* file = std::fopen(filename.c_str(), "wb");
assert(file);
+ calling = false;
+ expected = L" world";
+
// Test writing to a "non-terminal" stream does not call WriteConsoleW.
- std::__print::__vprint_unicode_windows(file, "Hello", std::make_format_args(), false, false);
+ std::__print::__vprint_unicode_windows<policy>(file, "Hello", std::make_format_args(), false, false);
assert(std::ftell(file) == 5);
// It's not possible to reliably test whether writing to a "terminal" stream
// flushes before writing. Testing flushing a closed stream worked on some
// platforms, but was unreliable.
calling = true;
- std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false, true);
+ std::__print::__vprint_unicode_windows<policy>(file, " world", std::make_format_args(), false, true);
+ std::fclose(file);
}
// When the output is a file the data is written as-is.
// When the output is a "terminal" invalid UTF-8 input is flagged.
+template <std::__print::__lock_policy policy>
static void test(std::wstring_view output, std::string_view input) {
// *** File ***
FILE* file = std::fopen(filename.c_str(), "wb");
assert(file);
- std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, false);
+ std::__print::__vprint_unicode_windows<policy>(file, input, std::make_format_args(), false, false);
assert(std::ftell(file) == static_cast<long>(input.size()));
std::fclose(file);
@@ -95,12 +102,13 @@ static void test(std::wstring_view output, std::string_view input) {
// *** Terminal ***
expected = output;
- std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false, true);
+ std::__print::__vprint_unicode_windows<policy>(file, input, std::make_format_args(), false, true);
}
+template <std::__print::__lock_policy policy>
static void test() {
// *** Test valid UTF-8 ***
-#define TEST(S) test(SV(S), S)
+#define TEST(S) test<policy>(SV(S), S)
TEST("hello world");
// copied from benchmarks/std_format_spec_string_unicode.bench.cpp
@@ -112,24 +120,29 @@ static void test() {
#undef TEST
// *** Test invalid utf-8 ***
- test(SV("\ufffd"), "\xc3");
- test(SV("\ufffd("), "\xc3\x28");
+ test<policy>(SV("\ufffd"), "\xc3");
+ test<policy>(SV("\ufffd("), "\xc3\x28");
// surrogate range
- test(SV("\ufffd"), "\xed\xa0\x80"); // U+D800
- test(SV("\ufffd"), "\xed\xaf\xbf"); // U+DBFF
- test(SV("\ufffd"), "\xed\xbf\x80"); // U+DC00
- test(SV("\ufffd"), "\xed\xbf\xbf"); // U+DFFF
+ test<policy>(SV("\ufffd"), "\xed\xa0\x80"); // U+D800
+ test<policy>(SV("\ufffd"), "\xed\xaf\xbf"); // U+DBFF
+ test<policy>(SV("\ufffd"), "\xed\xbf\x80"); // U+DC00
+ test<policy>(SV("\ufffd"), "\xed\xbf\xbf"); // U+DFFF
// beyond valid values
- test(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000
- test(SV("\ufffd"), "\xf4\xbf\xbf\xbf"); // U+11FFFF
+ test<policy>(SV("\ufffd"), "\xf4\x90\x80\x80"); // U+110000
+ test<policy>(SV("\ufffd"), "\xf4\xbf\xbf\xbf"); // U+11FFFF
// Validates http://unicode.org/review/pr-121.html option 3.
- test(SV("\u0061\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0062"), "\x61\xf1\x80\x80\xe1\x80\xc2\x62");
+ test<policy>(SV("\u0061\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u0062"), "\x61\xf1\x80\x80\xe1\x80\xc2\x62");
}
int main(int, char**) {
- test_basics();
- test();
+ test_basics<std::__print::__lock_policy::__manual>();
+ test<std::__print::__lock_policy::__manual>();
+
+ test_basics<std::__print::__lock_policy::__stdio>();
+ test<std::__print::__lock_policy::__stdio>();
+
+ return 0;
}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 167c79130bfbf..a26aeeebef75e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -764,7 +764,6 @@ queue deque
queue initializer_list
queue iosfwd
queue limits
-queue optional
queue stdexcept
queue string
queue string_view
@@ -831,7 +830,6 @@ regex deque
regex initializer_list
regex iosfwd
regex limits
-regex optional
regex stdexcept
regex string
regex string_view
@@ -1076,7 +1074,6 @@ thread iosfwd
thread istream
thread limits
thread locale
-thread optional
thread ratio
thread sstream
thread stdexcept
@@ -1148,7 +1145,6 @@ vector cwctype
vector initializer_list
vector iosfwd
vector limits
-vector optional
vector stdexcept
vector string
vector string_view
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 2bc61974a2ad8..00ab78e61a457 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -763,7 +763,6 @@ queue deque
queue initializer_list
queue iosfwd
queue limits
-queue optional
queue stdexcept
queue string
queue string_view
@@ -830,7 +829,6 @@ regex deque
regex initializer_list
regex iosfwd
regex limits
-regex optional
regex stdexcept
regex string
regex string_view
@@ -1075,7 +1073,6 @@ thread iosfwd
thread istream
thread limits
thread locale
-thread optional
thread ratio
thread sstream
thread stdexcept
@@ -1147,7 +1144,6 @@ vector cwctype
vector initializer_list
vector iosfwd
vector limits
-vector optional
vector stdexcept
vector string
vector string_view
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp
new file mode 100644
index 0000000000000..a5a35bf8bedb1
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode_buffered.file.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-filesystem
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Enable again
+// https://reviews.llvm.org/D150044
+// https://lab.llvm.org/buildbot/#/builders/237/builds/3578
+// UNSUPPORTED: asan, hwasan, msan
+
+// XFAIL: availability-fp_to_chars-missing
+
+// The error exception has no system error string.
+// XFAIL: LIBCXX-ANDROID-FIXME
+
+// <print>
+
+// void vprint_nonunicode_buffered(FILE* stream, string_view fmt, format_args args);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdio>
+#include <fstream>
+#include <iterator>
+#include <print>
+#include <string_view>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "print_tests.h"
+#include "test_macros.h"
+
+scoped_test_env env;
+std::string filename = env.create_file("output.txt");
+
+auto test_file = []<class... Args>(std::string_view expected, std::string_view fmt, Args&&... args) {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ std::vprint_nonunicode_buffered(file, fmt, std::make_format_args(args...));
+ std::fclose(file);
+
+ std::ifstream stream{filename.c_str(), std::ios_base::in | std::ios_base::binary};
+ std::string out(std::istreambuf_iterator<char>{stream}, {});
+ TEST_REQUIRE(out == expected,
+ TEST_WRITE_CONCATENATED(
+ "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>([[maybe_unused]] std::string_view what,
+ [[maybe_unused]] std::string_view fmt,
+ [[maybe_unused]] Args&&... args) {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::format_error,
+ [&]([[maybe_unused]] const std::format_error& e) {
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED(
+ "\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_nonunicode_buffered(file, fmt, std::make_format_args(args...)));
+
+ fclose(file);
+};
+
+// Glibc fails writing to a wide stream.
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+static void test_wide_stream() {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ int mode = std::fwide(file, 1);
+ assert(mode > 0);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::system_error,
+ [&]([[maybe_unused]] const std::system_error& e) {
+ [[maybe_unused]] std::string_view what{"failed to write formatted output"};
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_nonunicode_buffered(file, "hello", std::make_format_args()));
+}
+#endif // defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+
+static void test_read_only() {
+ FILE* file = fopen(filename.c_str(), "r");
+ assert(file);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::system_error,
+ [&]([[maybe_unused]] const std::system_error& e) {
+ [[maybe_unused]] std::string_view what{
+ "failed to write formatted output: " TEST_IF_AIX("Broken pipe", "Operation not permitted")};
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_nonunicode_buffered(file, "hello", std::make_format_args()));
+}
+
+static void test_new_line() {
+ // Text does newline translation.
+ {
+ FILE* file = fopen(filename.c_str(), "w");
+ assert(file);
+
+ std::vprint_nonunicode_buffered(file, "\n", std::make_format_args());
+#ifndef _WIN32
+ assert(std::ftell(file) == 1);
+#else
+ assert(std::ftell(file) == 2);
+#endif
+ }
+ // Binary no newline translation.
+ {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ std::vprint_nonunicode_buffered(file, "\n", std::make_format_args());
+ assert(std::ftell(file) == 1);
+ }
+}
+
+int main(int, char**) {
+ print_tests(test_file, test_exception);
+
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+ test_wide_stream();
+#endif
+ test_read_only();
+ test_new_line();
+
+ return 0;
+}
diff --git a/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp
new file mode 100644
index 0000000000000..23d77b5079a08
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode_buffered.file.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-filesystem
+// UNSUPPORTED: libcpp-has-no-unicode
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO PRINT Enable again
+// https://reviews.llvm.org/D150044
+// https://lab.llvm.org/buildbot/#/builders/237/builds/3578
+// UNSUPPORTED: asan, hwasan, msan
+
+// XFAIL: availability-fp_to_chars-missing
+
+// The error exception has no system error string.
+// XFAIL: LIBCXX-ANDROID-FIXME
+
+// <print>
+
+// void vprint_unicode_buffered(FILE* stream, string_view fmt, format_args args);
+
+// In the library when the stdout is redirected to a file it is no
+// longer considered a terminal and the special terminal handling is no
+// longer executed. There are tests in
+// libcxx/test/libcxx/input.output/iostream.format/print.fun/
+// to validate that behaviour
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdio>
+#include <fstream>
+#include <iterator>
+#include <print>
+#include <string_view>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "filesystem_test_helper.h"
+#include "print_tests.h"
+#include "test_macros.h"
+
+scoped_test_env env;
+std::string filename = env.create_file("output.txt");
+
+auto test_file = []<class... Args>(std::string_view expected, std::string_view fmt, Args&&... args) {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ std::vprint_unicode_buffered(file, fmt, std::make_format_args(args...));
+ std::fclose(file);
+
+ std::ifstream stream{filename.c_str(), std::ios_base::in | std::ios_base::binary};
+ std::string out(std::istreambuf_iterator<char>{stream}, {});
+ TEST_REQUIRE(out == expected,
+ TEST_WRITE_CONCATENATED(
+ "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
+};
+
+auto test_exception = []<class... Args>([[maybe_unused]] std::string_view what,
+ [[maybe_unused]] std::string_view fmt,
+ [[maybe_unused]] Args&&... args) {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::format_error,
+ [&]([[maybe_unused]] const std::format_error& e) {
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED(
+ "\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_unicode_buffered(file, fmt, std::make_format_args(args...)));
+
+ fclose(file);
+};
+
+// Glibc fails writing to a wide stream.
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+static void test_wide_stream() {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ int mode = std::fwide(file, 1);
+ assert(mode > 0);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::system_error,
+ [&]([[maybe_unused]] const std::system_error& e) {
+ [[maybe_unused]] std::string_view what{"failed to write formatted output"};
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_unicode_buffered(file, "hello", std::make_format_args()));
+}
+#endif // defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+
+static void test_read_only() {
+ FILE* file = fopen(filename.c_str(), "r");
+ assert(file);
+
+ TEST_VALIDATE_EXCEPTION(
+ std::system_error,
+ [&]([[maybe_unused]] const std::system_error& e) {
+ [[maybe_unused]] std::string_view what{
+ "failed to write formatted output: " TEST_IF_AIX("Broken pipe", "Operation not permitted")};
+ TEST_LIBCPP_REQUIRE(
+ e.what() == what,
+ TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
+ },
+ std::vprint_unicode_buffered(file, "hello", std::make_format_args()));
+}
+
+static void test_new_line() {
+ // Text does newline translation.
+ {
+ FILE* file = fopen(filename.c_str(), "w");
+ assert(file);
+
+ std::vprint_unicode_buffered(file, "\n", std::make_format_args());
+#ifndef _WIN32
+ assert(std::ftell(file) == 1);
+#else
+ assert(std::ftell(file) == 2);
+#endif
+ }
+ // Binary no newline translation.
+ {
+ FILE* file = fopen(filename.c_str(), "wb");
+ assert(file);
+
+ std::vprint_unicode_buffered(file, "\n", std::make_format_args());
+ assert(std::ftell(file) == 1);
+ }
+}
+
+int main(int, char**) {
+ print_tests(test_file, test_exception);
+
+#if defined(TEST_HAS_GLIBC) && !defined(TEST_HAS_NO_WIDE_CHARACTERS)
+ test_wide_stream();
+#endif
+ test_read_only();
+ test_new_line();
+
+ return 0;
+}
More information about the llvm-branch-commits
mailing list