public inbox for libstdc++-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).