diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2023-08-16 17:10:51 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2023-09-11 14:56:58 +0100 |
commit | f1dd83b720e0e7ce4e419f33a692a6df684708df (patch) | |
tree | 62576f1e0c753833bd8bf7a2965e3ca79dad133f | |
parent | 88a0a883960910530bfefa750461168f539f4a00 (diff) | |
download | gcc-f1dd83b720e0e7ce4e419f33a692a6df684708df.zip gcc-f1dd83b720e0e7ce4e419f33a692a6df684708df.tar.gz gcc-f1dd83b720e0e7ce4e419f33a692a6df684708df.tar.bz2 |
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.
-rw-r--r-- | libstdc++-v3/include/bits/version.def | 9 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/version.h | 25 | ||||
-rw-r--r-- | libstdc++-v3/include/std/stacktrace | 80 | ||||
-rw-r--r-- | libstdc++-v3/include/std/thread | 62 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/19_diagnostics/stacktrace/output.cc | 58 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/19_diagnostics/stacktrace/synopsis.cc | 3 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/19_diagnostics/stacktrace/version.cc | 6 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/30_threads/thread/id/hash.cc | 2 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/30_threads/thread/id/operators.cc | 1 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/30_threads/thread/id/operators_c++20.cc | 1 | ||||
-rw-r--r-- | libstdc++-v3/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 da8d067..8d9b2f7 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1527,6 +1527,15 @@ ftms = { }; ftms = { + name = formatters; + values = { + v = 202302; + cxxmin = 23; + hosted = yes; + }; +}; + +ftms = { name = ios_noreplace; values = { v = 202207; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 22cc211..3c20a8a 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 358a81b..da0e48d 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 28582c9..c182a4d 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 0000000..5116413 --- /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 262abea..ece5d52 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 ed466be..d59a069 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 6af80e5..afbae8f 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 88ec17c..c47a9e2 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 9432ec3..667b6a3 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 0000000..08d8c89 --- /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(); +} |