libstdc++: Implement std::formatter<std:🧵:id> without <sstream> [PR115099]
The std:🧵: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:🆔: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)
This commit is contained in:
parent
fc9fb69ad6
commit
4896bb3199
3 changed files with 63 additions and 12 deletions
|
@ -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>>;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,27 @@
|
|||
// { dg-require-gthreads "" }
|
||||
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
|
||||
#include <ostream>
|
||||
#include <format>
|
||||
|
||||
void
|
||||
test_no_includes(std::ostream& out)
|
||||
{
|
||||
std::thread::id i{};
|
||||
// Check stream insertion works without including <sstream>:
|
||||
out << i;
|
||||
#if __cpp_lib_formatters >= 202302
|
||||
// PR libstdc++/115099 - compilation error: format thread::id
|
||||
// Verify we can use std::thread::id with std::format without <sstream>:
|
||||
(void) std::format("{}", i);
|
||||
#ifdef _GLIBCXX_USE_WCHAR_T
|
||||
(void) std::format(L"{}", i);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#include <sstream>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
void
|
||||
|
|
Loading…
Add table
Reference in a new issue