public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-3841] libstdc++: Formatting std::thread::id and std::stacktrace (P2693R1)
@ 2023-09-11 13:57 Jonathan Wakely
0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2023-09-11 13:57 UTC (permalink / raw)
To: gcc-cvs, libstdc++-cvs
https://gcc.gnu.org/g:f1dd83b720e0e7ce4e419f33a692a6df684708df
commit r14-3841-gf1dd83b720e0e7ce4e419f33a692a6df684708df
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Wed Aug 16 17:10:51 2023 +0100
libstdc++: Formatting std::thread::id and std::stacktrace (P2693R1)
New std::formatter specializations for C++23.
libstdc++-v3/ChangeLog:
* include/bits/version.def (__cpp_lib_formatters): Define.
* include/bits/version.h: Regenerate.
* include/std/stacktrace (formatter<stacktrace_entry>)
(formatter<basic_stacktrace<Alloc>>): Define.
* include/std/thread (formatter<thread::id, charT>): Define.
* testsuite/19_diagnostics/stacktrace/output.cc: New test.
* testsuite/19_diagnostics/stacktrace/synopsis.cc: Add
std::formatter specializations.
* testsuite/19_diagnostics/stacktrace/version.cc: Check
__cpp_lib_formatters macro.
* testsuite/30_threads/thread/id/hash.cc: Remove gthreads
dependency.
* testsuite/30_threads/thread/id/operators.cc: Likewise.
* testsuite/30_threads/thread/id/operators_c++20.cc: Likewise.
* testsuite/30_threads/thread/id/output.cc: New test.
Diff:
---
libstdc++-v3/include/bits/version.def | 9 ++
libstdc++-v3/include/bits/version.h | 25 +++--
libstdc++-v3/include/std/stacktrace | 80 ++++++++++++++++
libstdc++-v3/include/std/thread | 62 +++++++++++++
.../testsuite/19_diagnostics/stacktrace/output.cc | 58 ++++++++++++
.../19_diagnostics/stacktrace/synopsis.cc | 3 +
.../testsuite/19_diagnostics/stacktrace/version.cc | 6 ++
.../testsuite/30_threads/thread/id/hash.cc | 2 -
.../testsuite/30_threads/thread/id/operators.cc | 1 -
.../30_threads/thread/id/operators_c++20.cc | 1 -
.../testsuite/30_threads/thread/id/output.cc | 103 +++++++++++++++++++++
11 files changed, 339 insertions(+), 11 deletions(-)
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index da8d067dd1ab..8d9b2f71a2e3 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1526,6 +1526,15 @@ ftms = {
};
};
+ftms = {
+ name = formatters;
+ values = {
+ v = 202302;
+ cxxmin = 23;
+ hosted = yes;
+ };
+};
+
ftms = {
name = ios_noreplace;
values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 22cc21119c56..3c20a8a51fb8 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1874,6 +1874,17 @@
#undef __glibcxx_want_adaptor_iterator_pair_constructor
// from version.def line 1530
+#if !defined(__cpp_lib_formatters)
+# if (__cplusplus >= 202302L) && _GLIBCXX_HOSTED
+# define __glibcxx_formatters 202302L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_formatters)
+# define __cpp_lib_formatters 202302L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_formatters) && defined(__glibcxx_want_formatters) */
+#undef __glibcxx_want_formatters
+
+// from version.def line 1539
#if !defined(__cpp_lib_ios_noreplace)
# if (__cplusplus >= 202302L) && _GLIBCXX_HOSTED
# define __glibcxx_ios_noreplace 202207L
@@ -1884,7 +1895,7 @@
#endif /* !defined(__cpp_lib_ios_noreplace) && defined(__glibcxx_want_ios_noreplace) */
#undef __glibcxx_want_ios_noreplace
-// from version.def line 1539
+// from version.def line 1548
#if !defined(__cpp_lib_move_only_function)
# if (__cplusplus >= 202302L) && _GLIBCXX_HOSTED
# define __glibcxx_move_only_function 202110L
@@ -1895,7 +1906,7 @@
#endif /* !defined(__cpp_lib_move_only_function) && defined(__glibcxx_want_move_only_function) */
#undef __glibcxx_want_move_only_function
-// from version.def line 1548
+// from version.def line 1557
#if !defined(__cpp_lib_spanstream)
# if (__cplusplus >= 202302L) && _GLIBCXX_HOSTED && (__glibcxx_span)
# define __glibcxx_spanstream 202106L
@@ -1906,7 +1917,7 @@
#endif /* !defined(__cpp_lib_spanstream) && defined(__glibcxx_want_spanstream) */
#undef __glibcxx_want_spanstream
-// from version.def line 1558
+// from version.def line 1567
#if !defined(__cpp_lib_stacktrace)
# if (__cplusplus >= 202302L) && _GLIBCXX_HOSTED && (_GLIBCXX_HAVE_STACKTRACE)
# define __glibcxx_stacktrace 202011L
@@ -1917,7 +1928,7 @@
#endif /* !defined(__cpp_lib_stacktrace) && defined(__glibcxx_want_stacktrace) */
#undef __glibcxx_want_stacktrace
-// from version.def line 1568
+// from version.def line 1577
#if !defined(__cpp_lib_string_contains)
# if (__cplusplus >= 202302L) && _GLIBCXX_HOSTED
# define __glibcxx_string_contains 202011L
@@ -1928,7 +1939,7 @@
#endif /* !defined(__cpp_lib_string_contains) && defined(__glibcxx_want_string_contains) */
#undef __glibcxx_want_string_contains
-// from version.def line 1577
+// from version.def line 1586
#if !defined(__cpp_lib_string_resize_and_overwrite)
# if (__cplusplus >= 202302L) && _GLIBCXX_HOSTED
# define __glibcxx_string_resize_and_overwrite 202110L
@@ -1939,7 +1950,7 @@
#endif /* !defined(__cpp_lib_string_resize_and_overwrite) && defined(__glibcxx_want_string_resize_and_overwrite) */
#undef __glibcxx_want_string_resize_and_overwrite
-// from version.def line 1586
+// from version.def line 1595
#if !defined(__cpp_lib_ratio)
# if (__cplusplus > 202302L)
# define __glibcxx_ratio 202306L
@@ -1950,7 +1961,7 @@
#endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
#undef __glibcxx_want_ratio
-// from version.def line 1594
+// from version.def line 1603
#if !defined(__cpp_lib_to_string)
# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars)
# define __glibcxx_to_string 202306L
diff --git a/libstdc++-v3/include/std/stacktrace b/libstdc++-v3/include/std/stacktrace
index 358a81b82e5d..da0e48d35329 100644
--- a/libstdc++-v3/include/std/stacktrace
+++ b/libstdc++-v3/include/std/stacktrace
@@ -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
diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread
index 28582c9df5ce..c182a4d56c1d 100644
--- a/libstdc++-v3/include/std/thread
+++ b/libstdc++-v3/include/std/thread
@@ -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
diff --git a/libstdc++-v3/testsuite/19_diagnostics/stacktrace/output.cc b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/output.cc
new file mode 100644
index 000000000000..5116413344d3
--- /dev/null
+++ b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/output.cc
@@ -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();
+}
diff --git a/libstdc++-v3/testsuite/19_diagnostics/stacktrace/synopsis.cc b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/synopsis.cc
index 262abea21ecf..ece5d526fb91 100644
--- a/libstdc++-v3/testsuite/19_diagnostics/stacktrace/synopsis.cc
+++ b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/synopsis.cc
@@ -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>>;
}
diff --git a/libstdc++-v3/testsuite/19_diagnostics/stacktrace/version.cc b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/version.cc
index ed466be5a362..d59a0696c256 100644
--- a/libstdc++-v3/testsuite/19_diagnostics/stacktrace/version.cc
+++ b/libstdc++-v3/testsuite/19_diagnostics/stacktrace/version.cc
@@ -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
diff --git a/libstdc++-v3/testsuite/30_threads/thread/id/hash.cc b/libstdc++-v3/testsuite/30_threads/thread/id/hash.cc
index 6af80e5c730f..afbae8f2b908 100644
--- a/libstdc++-v3/testsuite/30_threads/thread/id/hash.cc
+++ b/libstdc++-v3/testsuite/30_threads/thread/id/hash.cc
@@ -1,6 +1,4 @@
// { dg-do compile { target c++11 } }
-// { dg-require-cstdint "" }
-// { dg-require-gthreads "" }
// Copyright (C) 2010-2023 Free Software Foundation, Inc.
//
diff --git a/libstdc++-v3/testsuite/30_threads/thread/id/operators.cc b/libstdc++-v3/testsuite/30_threads/thread/id/operators.cc
index 88ec17c33beb..c47a9e262b47 100644
--- a/libstdc++-v3/testsuite/30_threads/thread/id/operators.cc
+++ b/libstdc++-v3/testsuite/30_threads/thread/id/operators.cc
@@ -1,5 +1,4 @@
// { dg-do compile { target c++11 } }
-// { dg-require-gthreads "" }
// Copyright (C) 2009-2023 Free Software Foundation, Inc.
//
diff --git a/libstdc++-v3/testsuite/30_threads/thread/id/operators_c++20.cc b/libstdc++-v3/testsuite/30_threads/thread/id/operators_c++20.cc
index 9432ec3468fb..667b6a3dcf5a 100644
--- a/libstdc++-v3/testsuite/30_threads/thread/id/operators_c++20.cc
+++ b/libstdc++-v3/testsuite/30_threads/thread/id/operators_c++20.cc
@@ -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.
//
diff --git a/libstdc++-v3/testsuite/30_threads/thread/id/output.cc b/libstdc++-v3/testsuite/30_threads/thread/id/output.cc
new file mode 100644
index 000000000000..08d8c899fda8
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/thread/id/output.cc
@@ -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();
+}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2023-09-11 13:57 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-09-11 13:57 [gcc r14-3841] libstdc++: Formatting std::thread::id and std::stacktrace (P2693R1) Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).