Tested aarch64-linux. Pushed to trunk. -- >8 --
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. --- 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 +++++++++++ .../19_diagnostics/stacktrace/output.cc | 58 ++++++++++ .../19_diagnostics/stacktrace/synopsis.cc | 3 + .../19_diagnostics/stacktrace/version.cc | 6 + .../testsuite/30_threads/thread/id/hash.cc | 2 - .../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(-) create mode 100644 libstdc++-v3/testsuite/19_diagnostics/stacktrace/output.cc create mode 100644 libstdc++-v3/testsuite/30_threads/thread/id/output.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index da8d067dd1a..8d9b2f71a2e 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/std/stacktrace b/libstdc++-v3/include/std/stacktrace index 358a81b82e5..da0e48d3532 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 28582c9df5c..c182a4d56c1 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 00000000000..5116413344d --- /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 262abea21ec..ece5d526fb9 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 ed466be5a36..d59a0696c25 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 6af80e5c730..afbae8f2b90 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 88ec17c33be..c47a9e262b4 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 9432ec3468f..667b6a3dcf5 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 00000000000..08d8c899fda --- /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(); +} -- 2.41.0