[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
Mon Mar 9 09:57:11 PDT 2026
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/185459
None
>From 378cd6cff8a37900f87d3a65b302d25d59cc3c1f 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 | 7 ++-
libcxx/include/print | 56 ++++++++-----------
libcxx/test/benchmarks/format/print.bench.cpp | 27 +++++++++
3 files changed, 55 insertions(+), 35 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 c5906c41b95b5..88a0f2ca06809 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -110,10 +110,13 @@ _LIBCPP_HIDE_FROM_ABI void __vprint_unicode(ostream& __os, string_view __fmt, fo
# endif // _LIBCPP_HAS_EXCEPTIONS
ostream::sentry __s(__os);
if (__s) {
+ auto __result = std::vformat(__fmt, __args);
+ if (__write_nl)
+ __result.push_back('\n');
# ifndef _LIBCPP_WIN32API
- __print::__vprint_unicode_posix(__file, __fmt, __args, __write_nl, true);
+ __print::__output_unicode_posix(__file, __result, true);
# elif _LIBCPP_HAS_WIDE_CHARACTERS
- __print::__vprint_unicode_windows(__file, __fmt, __args, __write_nl, true);
+ __print::__output_unicode_windows(__file, __result, true);
# else
# error "Windows builds with wchar_t disabled are not supported."
# endif
diff --git a/libcxx/include/print b/libcxx/include/print
index 0ff314c22dcd9..f127853080768 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -215,14 +215,10 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_terminal([[maybe_unused]] FILE* __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(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()) {
+ size_t __size = fwrite(__text.data(), 1, __text.size(), __stream);
+ if (__size < __text.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");
@@ -237,26 +233,24 @@ __vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args, bool
// 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) {
+_LIBCPP_HIDE_FROM_ABI inline void __output_unicode_posix(FILE* __stream, string_view __text, bool __is_terminal) {
// TODO PRINT Should flush errors throw too?
if (__is_terminal)
std::fflush(__stream);
- __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
+ __print::__output_nonunicode(__stream, __text);
}
# 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) {
+__vprint_unicode_windows(FILE* __stream, string_view __text, bool __is_terminal) {
if (!__is_terminal)
- return __print::__vprint_nonunicode(__stream, __fmt, __args, __write_nl);
+ return __print::__output_nonunicode(__stream, __text);
// 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.
//
@@ -266,11 +260,8 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
// 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
@@ -287,11 +278,7 @@ __vprint_unicode_windows(FILE* __stream, string_view __fmt, format_args __args,
# 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]
@@ -316,7 +303,7 @@ __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::__output_unicode_posix(__stream, __text, __print::__is_terminal(__stream));
# elif _LIBCPP_HAS_WIDE_CHARACTERS
__print::__vprint_unicode_windows(__stream, __fmt, __args, __write_nl, __print::__is_terminal(__stream));
# else
@@ -329,15 +316,16 @@ __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
}
@@ -349,16 +337,18 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
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
}
@@ -381,7 +371,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).
@@ -394,7 +384,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();
More information about the libcxx-commits
mailing list