@@ -1526,6 +1526,15 @@ ftms = {
};
};
+ftms = {
+ name = formatters;
+ values = {
+ v = 202302;
+ cxxmin = 23;
+ hosted = yes;
+ };
+};
+
ftms = {
name = ios_noreplace;
values = {
@@ -31,10 +31,12 @@
#include <bits/c++config.h>
#define __glibcxx_want_stacktrace
+#define __glibcxx_want_formatters
#include <bits/version.h>
#ifdef __cpp_lib_stacktrace // C++ >= 23 && hosted && HAVE_STACKTRACE
#include <compare>
+#include <format>
#include <new>
#include <string>
#include <sstream>
@@ -692,6 +694,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return std::move(__os).str();
}
+ template<>
+ class formatter<stacktrace_entry>
+ {
+ public:
+ constexpr typename basic_format_parse_context<char>::iterator
+ parse(basic_format_parse_context<char>& __pc)
+ {
+ __format::_Spec<char> __spec{};
+ const auto __last = __pc.end();
+ auto __first = __pc.begin();
+
+ auto __finalize = [this, &__spec] {
+ _M_spec = __spec;
+ };
+
+ auto __finished = [&] {
+ if (__first == __last || *__first == '}')
+ {
+ __finalize();
+ return true;
+ }
+ return false;
+ };
+
+ if (__finished())
+ return __first;
+
+ __first = __spec._M_parse_fill_and_align(__first, __last);
+ if (__finished())
+ return __first;
+
+ __first = __spec._M_parse_width(__first, __last, __pc);
+ if (__finished())
+ return __first;
+
+ __throw_format_error("format error: invalid format-spec for "
+ "std::stacktrace_entry");
+ }
+
+ template<typename _Out>
+ typename basic_format_context<_Out, char>::iterator
+ format(const stacktrace_entry& __x,
+ basic_format_context<_Out, char>& __fc) const
+ {
+ std::ostringstream __os;
+ __os << __x;
+ return __format::__write(__fc.out(), __os.view());
+ }
+
+ private:
+ __format::_Spec<char> _M_spec;
+ };
+
+ template<typename _Allocator>
+ class formatter<basic_stacktrace<_Allocator>>
+ {
+ public:
+ constexpr typename basic_format_parse_context<char>::iterator
+ parse(basic_format_parse_context<char>& __pc)
+ {
+ const auto __first = __pc.begin();
+ if (__first == __pc.end() || *__first == '}')
+ return __first;
+ __throw_format_error("format error: invalid format-spec for "
+ "std::basic_stacktrace");
+ }
+
+ template<typename _Out>
+ typename basic_format_context<_Out, char>::iterator
+ format(const basic_stacktrace<_Allocator>& __x,
+ basic_format_context<_Out, char>& __fc) const
+ {
+ std::ostringstream __os;
+ __os << __x;
+ return __format::__write(__fc.out(), __os.view());
+ }
+ };
+
namespace pmr
{
using stacktrace
@@ -42,10 +42,15 @@
# include <stop_token> // std::stop_source, std::stop_token, std::nostopstate
#endif
+#if __cplusplus >= 202302L
+# 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
#define __glibcxx_want_jthread
+#define __glibcxx_want_formatters
#include <bits/version.h>
namespace std _GLIBCXX_VISIBILITY(default)
@@ -281,6 +286,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
};
#endif // __cpp_lib_jthread
+#ifdef __cpp_lib_formatters // C++ >= 23
+
+ template<typename _CharT>
+ class formatter<thread::id, _CharT>
+ {
+ public:
+ constexpr typename basic_format_parse_context<_CharT>::iterator
+ parse(basic_format_parse_context<_CharT>& __pc)
+ {
+ __format::_Spec<_CharT> __spec{};
+ const auto __last = __pc.end();
+ auto __first = __pc.begin();
+
+ auto __finalize = [this, &__spec] {
+ _M_spec = __spec;
+ };
+
+ auto __finished = [&] {
+ if (__first == __last || *__first == '}')
+ {
+ __finalize();
+ return true;
+ }
+ return false;
+ };
+
+ if (__finished())
+ return __first;
+
+ __first = __spec._M_parse_fill_and_align(__first, __last);
+ if (__finished())
+ return __first;
+
+ __first = __spec._M_parse_width(__first, __last, __pc);
+ if (__finished())
+ return __first;
+
+ __throw_format_error("format error: invalid format-spec for "
+ "std::thread::id");
+ }
+
+ template<typename _Out>
+ 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(),
+ __fc, _M_spec);
+ }
+
+ private:
+ __format::_Spec<_CharT> _M_spec;
+ };
+#endif // __cpp_lib_formatters
+
/// @} group threads
_GLIBCXX_END_NAMESPACE_VERSION
new file mode 100644
@@ -0,0 +1,58 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+// { dg-require-effective-target stacktrace }
+
+#include <stacktrace>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+#ifndef __cpp_lib_formatters
+# error "Feature-test macro for formatters missing in <stacktrace>"
+#elif __cpp_lib_formatters < 202302L
+# error "Feature-test macro for formatters has wrong value in <stacktrace>"
+#endif
+
+void
+test_to_string()
+{
+ auto trace = std::stacktrace::current();
+ std::string s1 = std::to_string(trace.at(0));
+ VERIFY( s1.contains("test_to_string():15") );
+ std::string s2 = std::to_string(trace);
+ VERIFY( s2.contains(s1) );
+}
+
+void
+test_ostream()
+{
+ std::ostringstream out;
+ auto trace = std::stacktrace::current();
+ out << trace.at(0);
+ VERIFY( out.str() == std::to_string(trace.at(0)) );
+ out.str("");
+ out << trace;
+ VERIFY( out.str() == std::to_string(trace) );
+}
+
+void
+test_format()
+{
+ static_assert( std::is_default_constructible_v<std::formatter<std::stacktrace_entry, char>> );
+ static_assert( std::is_default_constructible_v<std::formatter<std::stacktrace, char>> );
+ static_assert( std::is_default_constructible_v<std::formatter<std::pmr::stacktrace, char>> );
+
+ auto trace = std::pmr::stacktrace::current();
+ VERIFY( std::format("{}", trace) == std::to_string(trace) );
+
+ std::stacktrace_entry entry = trace.at(0);
+ std::string str = std::to_string(entry);
+ VERIFY( std::format("{}", entry) == str );
+ VERIFY( std::format("{0:!<{1}}", entry, str.size() + 3) == (str + "!!!") );
+}
+
+int main()
+{
+ test_to_string();
+ test_ostream();
+ test_format();
+}
@@ -35,6 +35,9 @@ namespace std
ostream&
operator<<(ostream& os, const basic_stacktrace<Allocator>& st);
+ template<> struct formatter<stacktrace_entry>;
+ template<class Allocator> struct formatter<basic_stacktrace<Allocator>>;
+
namespace pmr {
using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
}
@@ -9,3 +9,9 @@
#elif __cpp_lib_stacktrace < 202011L
# error "Feature-test macro for stacktrace has wrong value in <version>"
#endif
+
+#ifndef __cpp_lib_formatters
+# error "Feature-test macro for formatters missing in <version>"
+#elif __cpp_lib_formatters < 202302L
+# error "Feature-test macro for formatters has wrong value in <version>"
+#endif
@@ -1,6 +1,4 @@
// { dg-do compile { target c++11 } }
-// { dg-require-cstdint "" }
-// { dg-require-gthreads "" }
// Copyright (C) 2010-2023 Free Software Foundation, Inc.
//
@@ -1,5 +1,4 @@
// { dg-do compile { target c++11 } }
-// { dg-require-gthreads "" }
// Copyright (C) 2009-2023 Free Software Foundation, Inc.
//
@@ -1,6 +1,5 @@
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
-// { dg-require-gthreads "" }
// Copyright (C) 2020-2023 Free Software Foundation, Inc.
//
new file mode 100644
@@ -0,0 +1,103 @@
+// { dg-do run { target c++11 } }
+// { dg-additional-options "-pthread" { target pthread } }
+// { dg-require-gthreads "" }
+
+#include <thread>
+#include <sstream>
+#include <format>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ std::ostringstream out;
+ std::thread::id i{}, j{};
+ out << i;
+ auto s0 = out.str();
+ VERIFY( s0 == "thread::id of a non-executing thread" );
+ out.str("");
+ out << j;
+ VERIFY( out.str() == s0 );
+
+ std::thread t1([]{});
+ j = t1.get_id();
+ out.str("");
+ out << j;
+ auto s1 = out.str();
+ VERIFY( s1 != s0 );
+ auto j2 = j;
+ out.str("");
+ out << j2;
+ VERIFY( out.str() == s1 );
+
+ std::thread t2([]{});
+ j2 = t2.get_id();
+ out.str("");
+ out << j2;
+ auto s2 = out.str();
+ VERIFY( s2 != s0 );
+ VERIFY( s2 != s1 );
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+ std::wostringstream wout;
+ wout << i;
+ auto ws0 = wout.str();
+ wout.str(L"");
+ wout << j;
+ auto ws1 = wout.str();
+ wout.str(L"");
+ wout << j2;
+ auto ws2 = wout.str();
+ wout.str(L"");
+
+ wout << s0.c_str() << ' ' << s1.c_str() << ' ' << s2.c_str();
+ VERIFY( wout.str() == (ws0 + L' ' + ws1 + L' ' + ws2) );
+#endif
+
+ t1.join();
+ t2.join();
+}
+
+void
+test02()
+{
+#if __cpp_lib_formatters >= 202302
+
+ static_assert( std::is_default_constructible_v<std::formatter<std::thread::id, char>> );
+
+ std::thread t1([]{});
+ std::thread t2([]{});
+ std::ostringstream out;
+ std::thread::id i{};
+ std::thread::id j = t1.get_id();
+ std::thread::id k = t2.get_id();
+ out << i << ' ' << j << ' ' << k;
+ VERIFY( std::format("{} {} {}", i, j, k) == out.str() );
+
+ out.str("");
+ out << j;
+ auto s1 = out.str();
+ auto len = s1.size();
+ out.str("");
+
+ auto s2 = std::format("{0:x^{1}}", j, len + 4);
+ VERIFY( s2 == ("xx" + s1 + "xx") );
+
+#ifdef _GLIBCXX_USE_WCHAR_T
+ static_assert( std::is_default_constructible_v<std::formatter<std::thread::id, wchar_t>> );
+ auto ws1 = std::format(L"{}", j);
+ VERIFY( ws1.length() == len );
+#endif
+
+ t1.join();
+ t2.join();
+#elif __cplusplus >= 202302L
+# error "Feature-test macro for formatters has wrong value in <thread>"
+#endif
+}
+
+int main()
+{
+ test01();
+ test02();
+}