[libcxx-commits] [libcxx] [libc++] Use std::to_chars to format thread::id (PR #181624)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Feb 25 07:02:27 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Nikolas Klauser (philnik777)
<details>
<summary>Changes</summary>
This makes printing `thread::id` faster, since we avoid a bunch of boilerplate code that isn't optimized away. It also avoids including `<sstream>` and `<__locale>`, cutting the time to parse `<thread>` in half.
This also changes the output when printing the id on a platform that uses a pointer as the underlying type. I don't think that's a problem, since the thread id isn't in any way stable.
---
Full diff: https://github.com/llvm/llvm-project/pull/181624.diff
8 Files Affected:
- (modified) libcxx/include/__thread/formatter.h (-4)
- (modified) libcxx/include/__thread/thread.h (+14-9)
- (modified) libcxx/include/future (+5)
- (modified) libcxx/include/module.modulemap.in (+4-1)
- (modified) libcxx/include/thread (+5)
- (modified) libcxx/test/libcxx/transitive_includes/cxx26.csv (-13)
- (modified) libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h (-17)
- (modified) libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.pass.cpp (-4)
``````````diff
diff --git a/libcxx/include/__thread/formatter.h b/libcxx/include/__thread/formatter.h
index 826607d47b469..73fe8f12c5bfe 100644
--- a/libcxx/include/__thread/formatter.h
+++ b/libcxx/include/__thread/formatter.h
@@ -61,10 +61,6 @@ struct formatter<__thread_id, _CharT> {
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..4933f44e89133 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
@@ -147,10 +147,15 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) {
// use a temporary stream instead and just output the thread
// id representation as a string.
- basic_ostringstream<_CharT, _Traits> __sstr;
- __sstr.imbue(locale::classic());
- __sstr << __id.__id_;
- return __os << __sstr.str();
+ using __int_type = __conditional_t<is_pointer<__libcpp_thread_t>::value, uintptr_t, __libcpp_thread_t>;
+
+ static const size_t __buffer_size = numeric_limits<__int_type>::digits10 + 1;
+ 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 4084148e52af6..a99a7ae486490 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 9012ed18cbd79..03c26f6c59c4c 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/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index c11fb5ac10016..d2667ece5123c 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -425,29 +425,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
@@ -1050,7 +1042,6 @@ system_error tuple
system_error version
thread array
thread atomic
-thread bitset
thread cctype
thread cerrno
thread climits
@@ -1065,14 +1056,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.tests.h b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/format.functions.tests.h
index f55f0e2af8cb2..d5efeec3f2740 100644
--- 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
@@ -22,7 +22,6 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
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);
@@ -37,22 +36,6 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
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);
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..350d8ab06199f 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
@@ -52,11 +52,7 @@ void test_format(StringViewT expected, std::thread::id arg) {
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
}
void test() {
``````````
</details>
https://github.com/llvm/llvm-project/pull/181624
More information about the libcxx-commits
mailing list