[libcxx-commits] [libcxx] [libc++] Refactor std::print to allow for constant folding of the format part (PR #185459)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Mar 13 03:00:12 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/185459
>From 2a84bf7843a044990a97b7195fef5f3dcd1e42f9 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 9 Mar 2026 17:28:55 +0100
Subject: [PATCH] [libc++] Refactor std::print to allow for constant folding of
the format part
---
libcxx/include/__ostream/print.h | 9 ++-
libcxx/include/print | 66 +++++++++----------
libcxx/test/benchmarks/format/print.bench.cpp | 27 ++++++++
.../print.fun/vprint_unicode_windows.pass.cpp | 4 +-
4 files changed, 63 insertions(+), 43 deletions(-)
create mode 100644 libcxx/test/benchmarks/format/print.bench.cpp
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index 6b6c9033d1e69..1c21ebce19e7d 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -110,11 +110,10 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
# endif // _LIBCPP_HAS_EXCEPTIONS
ostream::sentry __s(__os);
if (__s) {
-# if _LIBCPP_HAS_WIDE_CHARACTERS
- __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl);
-# else
-# error "Windows builds with wchar_t disabled are not supported."
-# endif
+ auto __result = std::vformat(__fmt, __args);
+ if (__write_nl)
+ __result.push_back('\n');
+ __print::__output_unicode_windows(__file, __result, true);
}
# if _LIBCPP_HAS_EXCEPTIONS
diff --git a/libcxx/include/print b/libcxx/include/print
index 5ea2f47ef197e..f3e7977ac6a54 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -207,20 +207,19 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __stream)
}
# endif // _LIBCPP_WIN32API
+[[noreturn]] _LIBCPP_HIDE_FROM_ABI inline void __handle_output_error(FILE* __stream) {
+ 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");
+}
+
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) {
+__output_nonunicode(FILE* __stream, string_view __text) {
_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");
- }
+ size_t __size = fwrite(__text.data(), 1, __text.size(), __stream);
+ if (__size < __text.size())
+ __print::__handle_output_error(__stream);
}
# if _LIBCPP_HAS_UNICODE
@@ -234,8 +233,7 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
# 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([[maybe_unused]] FILE* __stream, string_view __fmt, format_args __args, bool __write_nl) {
- string __str = std::vformat(__fmt, __args);
+__output_unicode_windows(FILE* __stream, string_view __text) {
// 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.
//
@@ -245,11 +243,8 @@ __vprint_unicode_windows([[maybe_unused]] FILE* __stream, string_view __fmt, for
// 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');
-
+ __format::__retarget_buffer<wchar_t> __buffer{__text.size()};
+ __unicode::__transcode(__text.begin(), __text.end(), __buffer.__make_output_iterator());
[[maybe_unused]] wstring_view __view = __buffer.__view();
// The macro _LIBCPP_TESTING_PRINT_WRITE_TO_WINDOWS_CONSOLE_FUNCTION is used to change
@@ -266,11 +261,7 @@ __vprint_unicode_windows([[maybe_unused]] FILE* __stream, string_view __fmt, for
# 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
-__vprint_unicode([[maybe_unused]] FILE* __stream,
- [[maybe_unused]] string_view __fmt,
- [[maybe_unused]] format_args __args,
- [[maybe_unused]] bool __write_nl) {
+_LIBCPP_HIDE_FROM_ABI inline void __output_unicode([[maybe_unused]] FILE* __stream, string_view __text) {
_LIBCPP_ASSERT_NON_NULL(__stream, "__stream must be a valid pointer to an output C stream");
// [print.fun]
@@ -295,14 +286,14 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
// Windows there is a different API. This API requires transcoding.
# ifndef _LIBCPP_WIN32API
- __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
+ __print::__output_nonunicode(__stream, __text);
# elif _LIBCPP_HAS_WIDE_CHARACTERS
if (__print::__is_terminal(__stream)) {
// TODO PRINT Should flush errors throw too?
std::fflush(__stream);
- __print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl);
+ __print::__output_unicode_windows(__stream, __text);
} else {
- __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
+ __print::__output_nonunicode(__stream, __text);
}
# else
# error "Windows builds with wchar_t disabled are not supported."
@@ -314,36 +305,39 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
} // namespace __print
template <class... _Args>
-_LIBCPP_HIDE_FROM_ABI void
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void
print(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, format_string<_Args...> __fmt, _Args&&... __args) {
+ auto __result = std::vformat(__fmt.get(), std::make_format_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);
+ __print::__output_unicode(__stream, __result);
else
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ __print::__output_nonunicode(__stream, __result);
# else // _LIBCPP_HAS_UNICODE
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
+ __print::__output_nonunicode(__stream, __result);
# endif // _LIBCPP_HAS_UNICODE
}
template <class... _Args>
-_LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __args) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_ALWAYS_INLINE void print(format_string<_Args...> __fmt, _Args&&... __args) {
std::print(stdout, __fmt, std::forward<_Args>(__args)...);
}
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI void
println(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, format_string<_Args...> __fmt, _Args&&... __args) {
+ auto __result = std::vformat(__fmt.get(), std::make_format_args(__args...));
+ __result.push_back('\n');
# if _LIBCPP_HAS_UNICODE
// 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);
+ __print::__output_unicode(__stream, __result);
else
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
+ __print::__output_nonunicode(__stream, __result);
# else // _LIBCPP_HAS_UNICODE
- __print::__vprint_nonunicode(__stream, __fmt.get(), std::make_format_args(__args...), true);
+ __print::__output_nonunicode(__stream, __result);
# endif // _LIBCPP_HAS_UNICODE
}
@@ -366,7 +360,7 @@ _LIBCPP_HIDE_FROM_ABI void println(format_string<_Args...> __fmt, _Args&&... __a
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* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
- __print::__vprint_unicode(__stream, __fmt, __args, false);
+ __print::__output_unicode(__stream, std::vformat(__fmt, __args));
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
@@ -379,7 +373,7 @@ _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* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
- __print::__vprint_nonunicode(__stream, __fmt, __args, false);
+ __print::__output_nonunicode(__stream, std::vformat(__fmt, __args));
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
diff --git a/libcxx/test/benchmarks/format/print.bench.cpp b/libcxx/test/benchmarks/format/print.bench.cpp
new file mode 100644
index 0000000000000..0f89129db393a
--- /dev/null
+++ b/libcxx/test/benchmarks/format/print.bench.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+#include <print>
+#include <string>
+
+#include "benchmark/benchmark.h"
+#include "make_string.h"
+#include "test_macros.h"
+
+template <class CharT>
+static void BM_string_without_formatting(benchmark::State& state) {
+ FILE* null = fopen("/dev/null", "w");
+ for (auto _ : state) {
+ std::print(null, "Hello, World!");
+ }
+}
+BENCHMARK(BM_string_without_formatting<char>)->Name("std::print(\"Hello, World!\")");
+
+BENCHMARK_MAIN();
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 162579027831b..474bef9829ef0 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
@@ -62,7 +62,7 @@ static void test_basics() {
assert(file);
calling = true;
- std::__print::__vprint_unicode_windows(file, " world", std::make_format_args(), false);
+ std::__print::__output_unicode_windows(file, " world");
}
// Invalid UTF-8 input is flagged.
@@ -71,7 +71,7 @@ static void test(std::wstring_view output, std::string_view input) {
assert(file);
expected = output;
- std::__print::__vprint_unicode_windows(file, input, std::make_format_args(), false);
+ std::__print::__output_unicode_windows(file, input);
assert(std::ftell(file) == 0);
std::fclose(file);
}
More information about the libcxx-commits
mailing list