[libcxx-commits] [libcxx] [libc++] Use std::to_chars to format thread::id and canonicalize the representation across platforms (PR #181624)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Mar 16 04:13:57 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/181624

>From 1a234983a62c98fd50cc522ff4010a06a1eb8297 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 16 Feb 2026 11:40:25 +0100
Subject: [PATCH] [libc++] Use std::to_string to format thread::id

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 +
 libcxx/include/__thread/formatter.h           |  9 +-
 libcxx/include/__thread/thread.h              | 28 +++---
 libcxx/include/future                         |  5 +
 libcxx/include/module.modulemap.in            |  5 +-
 libcxx/include/thread                         |  5 +
 .../format.functions.pass.cpp                 | 94 +++++++++++++++++++
 .../test/libcxx/transitive_includes/cxx26.csv | 13 ---
 .../format.functions.format.pass.cpp          | 57 -----------
 .../thread.thread.id/format.functions.tests.h | 83 ----------------
 .../format.functions.vformat.pass.cpp         | 48 +++++++---
 .../thread.thread.id/format.pass.cpp          | 16 ++--
 .../thread.thread.id/stream.pass.cpp          |  3 +-
 13 files changed, 175 insertions(+), 194 deletions(-)
 create mode 100644 libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
 delete mode 100644 libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
 delete mode 100644 libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 4441a8aed198c..5abb20d1fd78d 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -61,6 +61,9 @@ Deprecations and Removals
 Potentially breaking changes
 ----------------------------
 
+- The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
+  hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
+
 Announcements About Future Releases
 -----------------------------------
 
diff --git a/libcxx/include/__thread/formatter.h b/libcxx/include/__thread/formatter.h
index 826607d47b469..9682798e1df41 100644
--- a/libcxx/include/__thread/formatter.h
+++ b/libcxx/include/__thread/formatter.h
@@ -51,20 +51,13 @@ struct formatter<__thread_id, _CharT> {
     // On Linux systems pthread_t is an unsigned long long.
     // On Apple systems pthread_t is a pointer type.
     //
-    // Note the output should match what the stream operator does. Since
-    // the ostream operator has been shipped years before this formatter
-    // was added to the Standard, this formatter does what the stream
-    // operator does. This may require platform specific changes.
+    // Note the output should match what the stream operator does.
 
     using _Tp = decltype(__get_underlying_id(__id));
     using _Cp = conditional_t<integral<_Tp>, _Tp, conditional_t<is_pointer_v<_Tp>, uintptr_t, void>>;
     static_assert(!is_same_v<_Cp, void>, "unsupported thread::id type, please file a bug report");
 
     __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
-    if constexpr (is_pointer_v<_Tp>) {
-      __specs.__std_.__alternate_form_ = true;
-      __specs.__std_.__type_           = __format_spec::__type::__hexadecimal_lower_case;
-    }
     return __formatter::__format_integer(reinterpret_cast<_Cp>(__get_underlying_id(__id)), __ctx, __specs);
   }
 
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index b2f51aa816c10..516b4833fb925 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -11,12 +11,13 @@
 #define _LIBCPP___THREAD_THREAD_H
 
 #include <__assert>
+#include <__charconv/to_chars_integral.h>
 #include <__condition_variable/condition_variable.h>
 #include <__config>
 #include <__exception/terminate.h>
 #include <__functional/hash.h>
 #include <__functional/unary_function.h>
-#include <__locale>
+#include <__fwd/ostream.h>
 #include <__memory/addressof.h>
 #include <__memory/unique_ptr.h>
 #include <__mutex/mutex.h>
@@ -27,15 +28,14 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/forward.h>
+#include <cstdint>
+#include <limits>
 #include <tuple>
 
-#if _LIBCPP_HAS_LOCALIZATION
-#  include <sstream>
-#endif
-
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
@@ -144,13 +144,19 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) {
   //
   // Since various flags in the output stream can affect how the
   // thread id is represented (e.g. numpunct or showbase), we
-  // use a temporary stream instead and just output the thread
-  // id representation as a string.
+  // use `to_chars` directly and output the id as a string.
+
+  static_assert(is_pointer<__libcpp_thread_id>::value || is_integral<__libcpp_thread_id>::value);
+
+  using __int_type = __conditional_t<is_pointer<__libcpp_thread_id>::value, uintptr_t, __libcpp_thread_id>;
 
-  basic_ostringstream<_CharT, _Traits> __sstr;
-  __sstr.imbue(locale::classic());
-  __sstr << __id.__id_;
-  return __os << __sstr.str();
+  static const size_t __buffer_size = numeric_limits<__int_type>::digits10 + 2;
+  char __buffer[__buffer_size];
+  auto __ret =
+      std::__to_chars_integral(__buffer, __buffer + __buffer_size - 1, reinterpret_cast<__int_type>(__id.__id_), 10);
+  _LIBCPP_ASSERT_INTERNAL(__ret.__ec == std::errc(), "to_chars failed!");
+  *__ret.__ptr = '\0';
+  return __os << __buffer;
 }
 #  endif // _LIBCPP_HAS_LOCALIZATION
 
diff --git a/libcxx/include/future b/libcxx/include/future
index 6bd836afa04e8..17e84096fe160 100644
--- a/libcxx/include/future
+++ b/libcxx/include/future
@@ -2075,6 +2075,11 @@ _LIBCPP_POP_MACROS
 #    include <system_error>
 #    include <thread>
 #  endif
+
+#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#    include <sstream>
+#  endif
+
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_FUTURE
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d6e8289b7c8b0..1548121556ec9 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -948,7 +948,10 @@ module std [system] {
       header "__charconv/to_chars_integral.h"
       export std.charconv.to_chars_result
     }
-    module to_chars_result            { header "__charconv/to_chars_result.h" }
+    module to_chars_result            {
+      header "__charconv/to_chars_result.h"
+      export std.system_error.errc
+    }
     module traits                     { header "__charconv/traits.h" }
 
     header "charconv"
diff --git a/libcxx/include/thread b/libcxx/include/thread
index 029ed418e2070..e14e47d02788b 100644
--- a/libcxx/include/thread
+++ b/libcxx/include/thread
@@ -128,6 +128,11 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
 #    include <system_error>
 #    include <type_traits>
 #  endif
+
+#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+#    include <sstream>
+#  endif
+
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
 #endif // _LIBCPP_THREAD
diff --git a/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
new file mode 100644
index 0000000000000..6719ec6b783e6
--- /dev/null
+++ b/libcxx/test/libcxx/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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-threads
+
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// <thread>
+
+// template<class charT>
+// struct formatter<thread::id, charT>;
+
+// template<class... Args>
+//   string format(format_string<Args...> fmt, Args&&... args);
+// template<class... Args>
+//   wstring format(wformat_string<Args...> fmt, Args&&... args);
+
+#include <cassert>
+#include <format>
+#include <thread>
+
+#include "assert_macros.h"
+#include "concat_macros.h"
+#include "format.functions.common.h"
+#include "test_format_string.h"
+#include "test_macros.h"
+
+template <class CharT, class TestFunction>
+void format_tests(TestFunction check) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type specific part *****/
+  check(SV("0"), SV("{}"), input);
+  check(SV("0^42"), SV("{}^42"), input);
+  check(SV("0^42"), SV("{:}^42"), input);
+
+  // *** align-fill & width ***
+  check(SV("    0"), SV("{:5}"), input);
+  check(SV("0****"), SV("{:*<5}"), input);
+  check(SV("__0__"), SV("{:_^5}"), input);
+  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
+
+  check(SV("    0"), SV("{:{}}"), input, 5);
+  check(SV("0****"), SV("{:*<{}}"), input, 5);
+  check(SV("__0__"), SV("{:_^{}}"), input, 5);
+  check(SV("####0"), SV("{:#>{}}"), input, 5);
+}
+
+auto test = []<class CharT, class... Args>(
+                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+  {
+    std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+  {
+    std::basic_string<CharT> out = std::vformat(fmt.get(), std::make_format_args<context_t<CharT>>(args...));
+    TEST_REQUIRE(out == expected,
+                 TEST_WRITE_CONCATENATED(
+                     "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
+  }
+};
+
+int main(int, char**) {
+  format_tests<char>(test);
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  format_tests<wchar_t>(test);
+#endif
+
+  { // Check that we print the ID correctly (i.e. library-internal round-tripping works)
+    auto thread_id = std::this_thread::get_id();
+    auto result    = std::format("{}", thread_id);
+    auto num       = std::stoull(result);
+    using int_t =
+        std::conditional_t<std::is_pointer<std::__libcpp_thread_id>::value, uintptr_t, std::__libcpp_thread_id>;
+    assert(reinterpret_cast<int_t>(__get_underlying_id(std::this_thread::get_id())) == static_cast<int_t>(num));
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 253cf64703076..d130d6daa89f9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -424,29 +424,21 @@ functional tuple
 functional typeinfo
 functional unordered_map
 functional version
-future bitset
 future cctype
 future cerrno
 future climits
-future clocale
 future compare
-future cstddef
 future cstdint
 future cstdio
-future cstdlib
 future cstring
 future ctime
 future cwchar
 future cwctype
 future initializer_list
-future ios
 future iosfwd
-future istream
 future limits
 future ratio
-future sstream
 future stdexcept
-future streambuf
 future string
 future string_view
 future tuple
@@ -1037,7 +1029,6 @@ system_error tuple
 system_error version
 thread array
 thread atomic
-thread bitset
 thread cctype
 thread cerrno
 thread climits
@@ -1052,14 +1043,10 @@ thread ctime
 thread cwchar
 thread cwctype
 thread initializer_list
-thread ios
 thread iosfwd
-thread istream
 thread limits
 thread ratio
-thread sstream
 thread stdexcept
-thread streambuf
 thread string
 thread string_view
 thread tuple
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
deleted file mode 100644
index 5618836d89e80..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.format.pass.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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-threads
-
-// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
-
-// TODO FMT This test should not require std::to_chars(floating-point)
-// XFAIL: availability-fp_to_chars-missing
-
-// <thread>
-
-// template<class charT>
-// struct formatter<thread::id, charT>;
-
-// template<class... Args>
-//   string format(format_string<Args...> fmt, Args&&... args);
-// template<class... Args>
-//   wstring format(wformat_string<Args...> fmt, Args&&... args);
-
-#include <format>
-#include <cassert>
-
-#include "assert_macros.h"
-#include "concat_macros.h"
-#include "format.functions.tests.h"
-#include "test_format_string.h"
-#include "test_macros.h"
-
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
-
-auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
-  // After P2216 most exceptions thrown by std::format become ill-formed.
-  // Therefore this tests does nothing.
-};
-
-int main(int, char**) {
-  format_tests<char>(test, test_exception);
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
deleted file mode 100644
index f55f0e2af8cb2..0000000000000
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-#define TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
-
-#include <thread>
-
-#include "format.functions.common.h"
-#include "test_macros.h"
-
-template <class CharT, class TestFunction, class ExceptionTest>
-void format_tests(TestFunction check, ExceptionTest check_exception) {
-  // Note the output of std::thread::id is unspecified. The output text is the
-  // same as the stream operator. Since that format is already released this
-  // test follows the practice on existing systems.
-  std::thread::id input{};
-
-  /***** Test the type specific part *****/
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0"), SV("{}"), input);
-  check(SV("0^42"), SV("{}^42"), input);
-  check(SV("0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0"), SV("{:5}"), input);
-  check(SV("0****"), SV("{:*<5}"), input);
-  check(SV("__0__"), SV("{:_^5}"), input);
-  check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0"), SV("{:{}}"), input, 5);
-  check(SV("0****"), SV("{:*<{}}"), input, 5);
-  check(SV("__0__"), SV("{:_^{}}"), input, 5);
-  check(SV("####0"), SV("{:#>{}}"), input, 5);
-#else  // !defined(__APPLE__) && !defined(__FreeBSD__)
-  check(SV("0x0"), SV("{}"), input);
-  check(SV("0x0^42"), SV("{}^42"), input);
-  check(SV("0x0^42"), SV("{:}^42"), input);
-
-  // *** align-fill & width ***
-  check(SV("    0x0"), SV("{:7}"), input);
-  check(SV("0x0****"), SV("{:*<7}"), input);
-  check(SV("__0x0__"), SV("{:_^7}"), input);
-  check(SV("::::0x0"), SV("{::>7}"), input); // This is not a range, so : is allowed as fill character.
-
-  check(SV("    0x0"), SV("{:{}}"), input, 7);
-  check(SV("0x0****"), SV("{:*<{}}"), input, 7);
-  check(SV("__0x0__"), SV("{:_^{}}"), input, 7);
-  check(SV("####0x0"), SV("{:#>{}}"), input, 7);
-#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
-
-  /***** Test the type generic part *****/
-  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
-  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
-
-  // *** sign ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
-  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
-
-  // *** alternate form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
-
-  // *** zero-padding ***
-  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
-
-  // *** precision ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
-
-  // *** locale-specific form ***
-  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
-
-  // *** type ***
-  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
-    check_exception("The replacement field misses a terminating '}'", fmt, input);
-}
-
-#endif // TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
index 8555ebd742616..ffe1330fa9c7a 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.vformat.pass.cpp
@@ -22,21 +22,47 @@
 // string vformat(string_view fmt, format_args args);
 // wstring vformat(wstring_view fmt, wformat_args args);
 
-#include <format>
 #include <cassert>
+#include <format>
+#include <thread>
 
 #include "assert_macros.h"
 #include "concat_macros.h"
-#include "format.functions.tests.h"
+#include "format.functions.common.h"
 #include "test_macros.h"
 
-auto test = []<class CharT, class... Args>(
-                std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
-  std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
-  TEST_REQUIRE(out == expected,
-               TEST_WRITE_CONCATENATED(
-                   "\nFormat string   ", fmt, "\nExpected output ", expected, "\nActual output   ", out, '\n'));
-};
+template <class CharT, class ExceptionTest>
+void format_tests(ExceptionTest check_exception) {
+  // Note the output of std::thread::id is unspecified. The output text is the
+  // same as the stream operator. Since that format is already released this
+  // test follows the practice on existing systems.
+  std::thread::id input{};
+
+  /***** Test the type generic part *****/
+  check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
+  check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
+
+  // *** sign ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
+  check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
+
+  // *** alternate form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
+
+  // *** zero-padding ***
+  check_exception("The width option should not have a leading zero", SV("{:0}"), input);
+
+  // *** precision ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
+
+  // *** locale-specific form ***
+  check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
+
+  // *** type ***
+  for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
+    check_exception("The replacement field misses a terminating '}'", fmt, input);
+}
 
 auto test_exception =
     []<class CharT, class... Args>(
@@ -55,10 +81,10 @@ auto test_exception =
     };
 
 int main(int, char**) {
-  format_tests<char>(test, test_exception);
+  format_tests<char>(test_exception);
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  format_tests<wchar_t>(test, test_exception);
+  format_tests<wchar_t>(test_exception);
 #endif
 
   return 0;
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
index 39a257592809d..d3fc6e4b016d6 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp
@@ -34,9 +34,8 @@
 
 #define SV(S) MAKE_STRING_VIEW(CharT, S)
 
-template <class StringViewT>
-void test_format(StringViewT expected, std::thread::id arg) {
-  using CharT      = typename StringViewT::value_type;
+template <class CharT>
+std::basic_string<CharT> test_format(std::thread::id arg) {
   using String     = std::basic_string<CharT>;
   using OutIt      = std::back_insert_iterator<String>;
   using FormatCtxT = std::basic_format_context<OutIt, CharT>;
@@ -47,16 +46,15 @@ void test_format(StringViewT expected, std::thread::id arg) {
   OutIt out             = std::back_inserter(result);
   FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
   formatter.format(arg, format_ctx);
-  assert(result == expected);
+  assert(!result.empty());
+  return result;
 }
 
 template <class CharT>
 void test_fmt() {
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-  test_format(SV("0"), std::thread::id());
-#else
-  test_format(SV("0x0"), std::thread::id());
-#endif
+  auto def  = test_format<CharT>(std::thread::id());
+  auto self = test_format<CharT>(std::this_thread::get_id());
+  assert(def != self);
 }
 
 void test() {
diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
index 97e797f9a8fbb..3d4087918be63 100644
--- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
+++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp
@@ -10,7 +10,7 @@
 // UNSUPPORTED: no-localization
 // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
 
-// REQUIRES: locale.fr_FR.UTF-8
+// REQUIRES-: locale.fr_FR.UTF-8
 
 // TODO FMT This test should not require std::to_chars(floating-point)
 // XFAIL: availability-fp_to_chars-missing
@@ -27,6 +27,7 @@
 #include <format>
 #include <locale>
 #include <sstream>
+#include <string>
 #include <thread>
 
 #include "make_string.h"



More information about the libcxx-commits mailing list