From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id B55153858D38; Mon, 11 Sep 2023 13:57:07 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B55153858D38 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1694440627; bh=swmYK+wEoYN1H5yN0mBZOk9EHuS4q6DjkKegZEapzho=; h=From:To:Subject:Date:From; b=hxbLLUQhRwhTrP3uaUUThOPUHBvJF6xlYYf+7n627p9WdivloW9JRyvdR6meF4J51 xzx5tDzZWs62m+YSCEqI2gNr0mDtuU4TJ6MlmbPo2fCmTFs9oxC2pEGV+jBX0JETrx aCEO0PtyZJjBIfZMOWfJaTNMz2xns7Q8CLcgW2nk= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r14-3841] libstdc++: Formatting std::thread::id and std::stacktrace (P2693R1) X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: 88a0a883960910530bfefa750461168f539f4a00 X-Git-Newrev: f1dd83b720e0e7ce4e419f33a692a6df684708df Message-Id: <20230911135707.B55153858D38@sourceware.org> Date: Mon, 11 Sep 2023 13:57:07 +0000 (GMT) List-Id: https://gcc.gnu.org/g:f1dd83b720e0e7ce4e419f33a692a6df684708df commit r14-3841-gf1dd83b720e0e7ce4e419f33a692a6df684708df Author: Jonathan Wakely 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) (formatter>): Define. * include/std/thread (formatter): 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 #define __glibcxx_want_stacktrace +#define __glibcxx_want_formatters #include #ifdef __cpp_lib_stacktrace // C++ >= 23 && hosted && HAVE_STACKTRACE #include +#include #include #include #include @@ -692,6 +694,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return std::move(__os).str(); } + template<> + class formatter + { + public: + constexpr typename basic_format_parse_context::iterator + parse(basic_format_parse_context& __pc) + { + __format::_Spec __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 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 _M_spec; + }; + + template + class formatter> + { + public: + constexpr typename basic_format_parse_context::iterator + parse(basic_format_parse_context& __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 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 // std::stop_source, std::stop_token, std::nostopstate #endif +#if __cplusplus >= 202302L +# include +#endif + #include // std::thread, get_id, yield #include // std::this_thread::sleep_for, sleep_until #define __glibcxx_want_jthread +#define __glibcxx_want_formatters #include namespace std _GLIBCXX_VISIBILITY(default) @@ -281,6 +286,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; #endif // __cpp_lib_jthread +#ifdef __cpp_lib_formatters // C++ >= 23 + + template + class formatter + { + 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 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 +#include +#include + +#ifndef __cpp_lib_formatters +# error "Feature-test macro for formatters missing in " +#elif __cpp_lib_formatters < 202302L +# error "Feature-test macro for formatters has wrong value in " +#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> ); + static_assert( std::is_default_constructible_v> ); + static_assert( std::is_default_constructible_v> ); + + 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& st); + template<> struct formatter; + template struct formatter>; + namespace pmr { using stacktrace = basic_stacktrace>; } 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 " #endif + +#ifndef __cpp_lib_formatters +# error "Feature-test macro for formatters missing in " +#elif __cpp_lib_formatters < 202302L +# error "Feature-test macro for formatters has wrong value in " +#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 +#include +#include +#include + +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::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> ); + 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 " +#endif +} + +int main() +{ + test01(); + test02(); +}