[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