[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