diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-05-17 10:55:32 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2024-05-22 10:06:52 +0100 |
commit | 4896bb3199253dc350f8fb5ff63370310ca27ce2 (patch) | |
tree | eaeb7ef6474cc1204bb1f49cd70ec8275b608e5a /libstdc++-v3/include | |
parent | fc9fb69ad624fd4cc89ff31ad0a7b8d88481c1f7 (diff) | |
download | gcc-4896bb3199253dc350f8fb5ff63370310ca27ce2.zip gcc-4896bb3199253dc350f8fb5ff63370310ca27ce2.tar.gz gcc-4896bb3199253dc350f8fb5ff63370310ca27ce2.tar.bz2 |
libstdc++: Implement std::formatter<std::thread::id> without <sstream> [PR115099]
The std::thread::id formatter uses std::basic_ostringstream without
including <sstream>, which went unnoticed because the test for it uses
a stringstream to check the output is correct.
The fix implemented here is to stop using basic_ostringstream for
formatting thread::id and just use std::format instead.
As a drive-by fix, the formatter specialization is constrained to
require that the thread::id::native_handle_type can be formatted, to
avoid making the formatter ill-formed if the pthread_t type is not a
pointer or integer. Since non-void pointers can't be formatted, ensure
that we convert pointers to const void* for formatting. Make a similar
change to the existing operator<< overload so that in the unlikely case
that pthread_t is a typedef for char* we don't treat it as a
null-terminated string when inserting into a stream.
libstdc++-v3/ChangeLog:
PR libstdc++/115099
* include/bits/std_thread.h: Declare formatter as friend of
thread::id.
* include/std/thread (operator<<): Convert non-void pointers to
void pointers for output.
(formatter): Add constraint that thread::native_handle_type is a
pointer or integer.
(formatter::format): Reimplement without basic_ostringstream.
* testsuite/30_threads/thread/id/output.cc: Check output
compiles before <sstream> has been included.
(cherry picked from commit 1a5e4dd83788ea4c049d354d83ad58a6a3d747e6)
Diffstat (limited to 'libstdc++-v3/include')
-rw-r--r-- | libstdc++-v3/include/bits/std_thread.h | 11 | ||||
-rw-r--r-- | libstdc++-v3/include/std/thread | 43 |
2 files changed, 43 insertions, 11 deletions
diff --git a/libstdc++-v3/include/bits/std_thread.h b/libstdc++-v3/include/bits/std_thread.h index 2d7df12..5817bfb 100644 --- a/libstdc++-v3/include/bits/std_thread.h +++ b/libstdc++-v3/include/bits/std_thread.h @@ -53,6 +53,10 @@ namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +#if __glibcxx_formatters + template<typename, typename> class formatter; +#endif + /** @addtogroup threads * @{ */ @@ -117,13 +121,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<class _CharT, class _Traits> friend basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __out, id __id); + +#if __glibcxx_formatters + friend formatter<id, char>; + friend formatter<id, wchar_t>; +#endif }; private: id _M_id; // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 2097. packaged_task constructors should be constrained + // 2097. packaged_task constructors should be constrained // 3039. Unnecessary decay in thread and packaged_task template<typename _Tp> using __not_same = __not_<is_same<__remove_cvref_t<_Tp>, thread>>; diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index 09ca311..e994d68 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -42,10 +42,6 @@ # include <stop_token> // std::stop_source, std::stop_token, std::nostopstate #endif -#if __cplusplus > 202002L -# include <format> -#endif - #include <bits/std_thread.h> // std::thread, get_id, yield #include <bits/this_thread_sleep.h> // std::this_thread::sleep_for, sleep_until @@ -53,6 +49,10 @@ #define __glibcxx_want_formatters #include <bits/version.h> +#if __cpp_lib_formatters +# include <format> +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -104,10 +104,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id) { + // Convert non-void pointers to const void* for formatted output. + using __output_type + = __conditional_t<is_pointer<thread::native_handle_type>::value, + const void*, + thread::native_handle_type>; + if (__id == thread::id()) return __out << "thread::id of a non-executing thread"; else - return __out << __id._M_thread; + return __out << static_cast<__output_type>(__id._M_thread); } /// @} @@ -287,8 +293,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif // __cpp_lib_jthread #ifdef __cpp_lib_formatters // C++ >= 23 - template<typename _CharT> + requires is_pointer_v<thread::native_handle_type> + || is_integral_v<thread::native_handle_type> class formatter<thread::id, _CharT> { public: @@ -331,10 +338,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename basic_format_context<_Out, _CharT>::iterator format(thread::id __id, basic_format_context<_Out, _CharT>& __fc) const { - std::basic_ostringstream<_CharT> __os; - __os << __id; - auto __str = __os.view(); - return __format::__write_padded_as_spec(__str, __str.size(), + basic_string_view<_CharT> __sv; + if constexpr (is_same_v<_CharT, char>) + __sv = "{}thread::id of a non-executing thread"; + else + __sv = L"{}thread::id of a non-executing thread"; + basic_string<_CharT> __str; + if (__id == thread::id()) + __sv.remove_prefix(2); + else + { + using _FmtStr = __format::_Runtime_format_string<_CharT>; + // Convert non-void pointers to const void* for formatted output. + using __output_type + = __conditional_t<is_pointer_v<thread::native_handle_type>, + const void*, + thread::native_handle_type>; + auto __o = static_cast<__output_type>(__id._M_thread); + __sv = __str = std::format(_FmtStr(__sv.substr(0, 2)), __o); + } + return __format::__write_padded_as_spec(__sv, __sv.size(), __fc, _M_spec, __format::_Align_right); } |