aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/include
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-05-17 10:55:32 +0100
committerJonathan Wakely <jwakely@redhat.com>2024-05-22 10:06:52 +0100
commit4896bb3199253dc350f8fb5ff63370310ca27ce2 (patch)
treeeaeb7ef6474cc1204bb1f49cd70ec8275b608e5a /libstdc++-v3/include
parentfc9fb69ad624fd4cc89ff31ad0a7b8d88481c1f7 (diff)
downloadgcc-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.h11
-rw-r--r--libstdc++-v3/include/std/thread43
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);
}