[PATCH v4] libstdc++: Implement debug format for strings and characters formatters [PR109162]

Jonathan Wakely jwakely@redhat.com
Thu Apr 10 16:39:31 GMT 2025


On 10/04/25 11:24 +0200, Tomasz Kamiński wrote:
>This patch implements part P2286R8 that specified debug (escaped)
>format for the strings and characters sequences. This include both
>handling of the '?' format specifier and set_debug_format member.
>
>To indicate partial support we define __glibcxx_format_ranges macro
>value 1, without defining __cpp_lib_format_ranges.
>
>We provide two separate escaping routines depending on the literal
>encoding for the corresponding character types. If the character
>encoding is Unicode, we follow the specification for the standard
>(__format::__write_escaped_unicode).
>For other encodings, we escape only characters in range [0x00, 0x80),
>interpreting them as ACII values: [0x00, 0x20), 0x7f and  '\t', '\r',

"ASCII"

>'\n', '\\', '"', '\'' are escaped. We assume every character outside
>this range is printable (__format::_write_escaped_ascii).
>In particular we do not yet implement special handling of shift
>sequences.
>
>For Unicode escaping a new __escape_edges table is introduced,
>that encodes information if character belongs to General_Category
>that is escaped by the standard (Control or Other). This table
>is generated from DerivedGeneralCategory.txt provided by Unicode.
>Only boolean flag is preserved to reduce the number of entires.

"entries"

>The additional rules for escaping are handled by __should_escape_unicode.
>
>When width or precision is specified, we emit escaped string to the temporary
>buffer and format the resulting string according to the format spec.
>For characters use a fixed size stack buffer, for which a new _Fixedbuf_sink is
>introduced. For strings, we use _Str_sink and to avoid allocations,
>we compute the estimated size of (possibly truncated) input, and if it is
>larger than width field we print directly.
>
>	PR libstdc++/109162
>
>contrib/ChangeLog:
>
>	* unicode/README: Mentioned DerivedGeneralCategory.txt.
>	* unicode/gen_libstdcxx_unicode_data.py: Generation __escape_edges
>	table from DerivedGeneralCategory.txt. Update file name in comments.
>	* unicode/DerivedGeneralCategory.txt: Copy of file distrubuted by

"distributed"

>	Unicode Consortium.
>	ftp://ftp.unicode.org/Public/UNIDATA/extracted/DerivedGeneralCategory.txt.


I still don't think we want the URL in the ChangeLog.

>libstdc++-v3/ChangeLog:
>
>	* include/bits/chrono_io.h (__detail::_Widen): Moved to std/format file.
>	* include/bits/unicode-data.h: Regnerate.
>	* include/bits/unicode.h (__unicode::_Utf_iterator::_M_units)
>	(__unicode::__should_escape_category): Define.

What happened to the changes to bits/version.def and bits/version.h ?

I thought you were going to change version.def to use no_stdname but
now it's not in the patch at all.

>	* include/std/format (_GLIBCXX_WIDEN_, _GLIBCXX_WIDEN):	Copied from
>	include/bits/chrono_io.h.
>	(__format::_Widen): Moved from include/bits/chrono_io.h.
>	(__format::_Term_char, __format::_Escapes, __format::_Separators)
>	(__format::__should_escape_ascii, __format::__should_escape_unicode)
>	(__format::__write_escape_seq, __format::__write_escaped_char)
>	(__format::__write_escaped_acii, __format::__write_escaped_unicode)
>	(__format::__write_escaped): Define.
>	(__formatter_str::_S_format): Extracted truncation of character
>	sequences.
>	(__formatter_str::format): Handle _Pres_esc.
>	(__formatter_int::_M_do_parse) [__glibcxx_format_ranges]: Parse '?'.
>	(__formatter_int::_M_format_character_escaped): Define.
>	(formatter<_CharT, _CharT>::format, formatter<char, wchar_t>::format):
>	Handle _Pres_esc.
>	(__formatter_str::set_debug_format, formatter<...>::set_debug_format)
>	Guard with __glibcxx_format_ranges.
>	(__format::_Fixedbuf_sink): Define.
>	* testsuite/std/format/debug.cc: New test.
>	* testsuite/std/format/debug_nonunicode.cc: New test.
>	* testsuite/std/format/parse_ctx.cc (escaped_strings_supported): Define
>	to true if __glibcxx_format_ranges is defined.
>	* testsuite/std/format/string.cc (escaped_strings_supported): Define to
>	 true if __glibcxx_format_ranges is defined.
>---
> This I believe address all review suggestions. I have also followed
> Patrick's suggestions and added debug_nonunicode.cc test file.
> Which helped to surface problem, where _GLIBCXX_WIDEN("\FFFFD") was
> ill-formed for non-unicode encodings.
>
> contrib/unicode/DerivedGeneralCategory.txt    | 4323 +++++++++++++++++
> contrib/unicode/README                        |    3 +-
> contrib/unicode/gen_libstdcxx_unicode_data.py |   47 +-
> libstdc++-v3/include/bits/chrono_io.h         |   16 +-
> libstdc++-v3/include/bits/unicode-data.h      |  260 +-
> libstdc++-v3/include/bits/unicode.h           |   17 +
> libstdc++-v3/include/std/format               |  492 +-
> .../23_containers/vector/bool/format.cc       |    3 +-
> libstdc++-v3/testsuite/std/format/debug.cc    |  454 ++
> .../testsuite/std/format/debug_nounicode.cc   |    5 +
> .../testsuite/std/format/parse_ctx.cc         |    2 +-
> libstdc++-v3/testsuite/std/format/string.cc   |    2 +-
> 12 files changed, 5538 insertions(+), 86 deletions(-)
> create mode 100644 contrib/unicode/DerivedGeneralCategory.txt
> create mode 100644 libstdc++-v3/testsuite/std/format/debug.cc
> create mode 100644 libstdc++-v3/testsuite/std/format/debug_nounicode.cc

[snip]

>diff --git a/libstdc++-v3/include/bits/unicode.h b/libstdc++-v3/include/bits/unicode.h
>index 99d972eccff..f1b6bf49c54 100644
>--- a/libstdc++-v3/include/bits/unicode.h
>+++ b/libstdc++-v3/include/bits/unicode.h
>@@ -150,6 +150,11 @@ namespace __unicode
>       base() const requires forward_iterator<_Iter>
>       { return _M_curr(); }
>
>+      [[nodiscard]]
>+      constexpr iter_difference_t<_Iter>
>+      _M_units() const requires forward_iterator<_Iter>
>+      { return _M_to_increment; }
>+
>       [[nodiscard]]
>       constexpr value_type
>       operator*() const { return _M_buf[_M_buf_index]; }
>@@ -609,6 +614,18 @@ inline namespace __v16_0_0
>     return (__p - __width_edges) % 2 + 1;
>   }
>
>+  // @pre c <= 0x10FFFF
>+  constexpr bool
>+  __should_escape_category(char32_t __c) noexcept
>+  {
>+    constexpr uint32_t __mask = 0x01;
>+    auto* __end = std::end(__escape_edges);
>+    auto* __p = std::lower_bound(__escape_edges, __end,
>+				 (__c << 1u) + 2);
>+    return __p[-1] & __mask;
>+  }
>+
>+
>   // @pre c <= 0x10FFFF
>   constexpr _Gcb_property
>   __grapheme_cluster_break_property(char32_t __c) noexcept
>diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
>index 2e9319cdda6..b905d8c012d 100644
>--- a/libstdc++-v3/include/std/format
>+++ b/libstdc++-v3/include/std/format
>@@ -80,8 +80,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> /// @cond undocumented
> namespace __format
> {
>-  // Type-erased character sink.
>+  // STATICALLY-WIDEN, see C++20 [time.general]
>+  // It doesn't matter for format strings (which can only be char or wchar_t)
>+  // but this returns the narrow string for anything that isn't wchar_t. This
>+  // is done because const char* can be inserted into any ostream type, and
>+  // will be widened at runtime if necessary.
>+  template<typename _CharT>
>+    consteval auto
>+    _Widen(const char* __narrow, const wchar_t* __wide)
>+    {
>+      if constexpr (is_same_v<_CharT, wchar_t>)
>+	return __wide;
>+      else
>+	return __narrow;
>+    }
>+#define _GLIBCXX_WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
>+#define _GLIBCXX_WIDEN(S) _GLIBCXX_WIDEN_(_CharT, S)
>+
>+  // Type-erased character sinks.
>   template<typename _CharT> class _Sink;
>+  template<typename _CharT> class _Fixedbuf_sink;
>+  template<typename _Seq> class _Seq_sink;
>+
>+  template<typename _CharT, typename _Alloc = allocator<_CharT>>
>+    using _Str_sink
>+      = _Seq_sink<basic_string<_CharT, char_traits<_CharT>, _Alloc>>;
>+
>+  // template<typename _CharT, typename _Alloc = allocator<_CharT>>
>+  // using _Vec_sink = _Seq_sink<vector<_CharT, _Alloc>>;
>+
>   // Output iterator that writes to a type-erase character sink.
>   template<typename _CharT>
>     class _Sink_iter;
>@@ -848,6 +875,286 @@ namespace __format
> 				      __spec._M_fill);
>     }
>
>+  // Valus are indicies into _Escapes::all.

"Values" and "indices"

>+  enum class _Term_char : unsigned char {
>+    _Tc_quote = 12,
>+    _Tc_apos = 15
>+  };
>+
>+  template<typename _CharT>
>+    struct _Escapes
>+    {
>+      using _Str_view = basic_string_view<_CharT>;
>+
>+      static consteval
>+      _Str_view _S_all()
>+      { return _GLIBCXX_WIDEN("\t\\t\n\\n\r\\r\\\\\\\"\\\"'\\'\\u\\x"); }
>+
>+      static constexpr
>+      _CharT _S_term(_Term_char __term)
>+      { return _S_all()[static_cast<unsigned char>(__term)]; }
>+
>+      static consteval
>+      _Str_view _S_tab()
>+      { return _S_all().substr(0, 3); }
>+
>+      static consteval
>+      _Str_view _S_newline()
>+      { return _S_all().substr(3, 3); }
>+
>+      static consteval
>+      _Str_view _S_return()
>+      { return _S_all().substr(6, 3); }
>+
>+      static consteval
>+      _Str_view _S_bslash()
>+      { return _S_all().substr(9, 3); }
>+
>+      static consteval
>+      _Str_view _S_quote()
>+      { return _S_all().substr(12, 3); }
>+
>+      static consteval
>+      _Str_view _S_apos()
>+      { return _S_all().substr(15, 3); }
>+
>+      static consteval
>+      _Str_view _S_u()
>+      { return _S_all().substr(18, 2); }
>+
>+      static consteval
>+      _Str_view _S_x()
>+      { return _S_all().substr(20, 2); }
>+    };
>+
>+  template<typename _CharT>
>+    struct _Separators
>+    {
>+      using _Str_view = basic_string_view<_CharT>;
>+
>+      static consteval
>+      _Str_view _S_all()
>+      { return _GLIBCXX_WIDEN("{}"); }
>+
>+      static consteval
>+      _Str_view _S_braces()
>+      { return _S_all().substr(0, 2); }
>+    };
>+
>+  template<typename _CharT>
>+    constexpr bool __should_escape_ascii(_CharT __c, _Term_char __term)
>+    {
>+      using _Esc = _Escapes<_CharT>;
>+      switch (__c)
>+	{
>+	  case _Esc::_S_tab()[0]:
>+	  case _Esc::_S_newline()[0]:
>+	  case _Esc::_S_return()[0]:
>+	  case _Esc::_S_bslash()[0]:
>+	    return true;
>+	  case _Esc::_S_quote()[0]:
>+	    return __term == _Term_char::_Tc_quote;
>+	  case _Esc::_S_apos()[0]:
>+	    return __term == _Term_char::_Tc_apos;
>+	  default:
>+	    return (__c >= 0 && __c < 0x20) || __c == 0x7f;
>+	};
>+  }
>+
>+  // @pre __c <= 0x10FFFF
>+  constexpr bool __should_escape_unicode(char32_t __c, bool __prev_esc)
>+  {
>+    if (__unicode::__should_escape_category(__c))
>+      return __c != U' ';
>+    if (!__prev_esc)
>+      return false;
>+    return __unicode::__grapheme_cluster_break_property(__c)
>+	     == __unicode::_Gcb_property::_Gcb_Extend;
>+  }
>+
>+  using uint_least32_t = __UINT_LEAST32_TYPE__;
>+  template<typename _Out, typename _CharT>
>+    _Out
>+    __write_escape_seq(_Out __out, uint_least32_t __val,
>+		       basic_string_view<_CharT> __prefix)
>+    {
>+      using _Str_view = basic_string_view<_CharT>;
>+      constexpr size_t __max = 8;
>+      char __buf[__max];
>+      const string_view __narrow(
>+	__buf,
>+	std::__to_chars_i<uint_least32_t>(__buf, __buf + __max, __val, 16).ptr);
>+
>+      __out = __format::__write(__out, __prefix);
>+      *__out = _Separators<_CharT>::_S_braces()[0];
>+      ++__out;
>+      if constexpr (is_same_v<char, _CharT>)
>+	__out = __format::__write(__out, __narrow);
>+#ifdef _GLIBCXX_USE_WCHAR_T
>+      else
>+	{
>+	  _CharT __wbuf[__max];
>+	  const size_t __n = __narrow.size();
>+	  std::__to_wstring_numeric(__narrow.data(), __n, __wbuf);
>+	  __out = __format::__write(__out, _Str_view(__wbuf, __n));
>+	}
>+#endif
>+      *__out = _Separators<_CharT>::_S_braces()[1];
>+      return ++__out;
>+    }
>+
>+  template<typename _Out, typename _CharT>
>+    _Out
>+    __write_escaped_char(_Out __out, _CharT __c)
>+    {
>+      using _UChar = make_unsigned_t<_CharT>;
>+      using _Esc = _Escapes<_CharT>;
>+      switch (__c)
>+	{
>+	  case _Esc::_S_tab()[0]:
>+	    return __format::__write(__out, _Esc::_S_tab().substr(1, 2));
>+	  case _Esc::_S_newline()[0]:
>+	    return __format::__write(__out, _Esc::_S_newline().substr(1, 2));
>+	  case _Esc::_S_return()[0]:
>+	    return __format::__write(__out, _Esc::_S_return().substr(1, 2));
>+	  case _Esc::_S_bslash()[0]:
>+	    return __format::__write(__out, _Esc::_S_bslash().substr(1, 2));
>+	  case _Esc::_S_quote()[0]:
>+	    return __format::__write(__out, _Esc::_S_quote().substr(1, 2));
>+	  case _Esc::_S_apos()[0]:
>+	    return __format::__write(__out, _Esc::_S_apos().substr(1, 2));
>+	  default:
>+	    return __format::__write_escape_seq(__out,
>+				static_cast<_UChar>(__c),
>+						_Esc::_S_u());
>+	}
>+    }
>+
>+  template<typename _CharT, typename _Out>
>+    _Out
>+    __write_escaped_ascii(_Out __out,
>+			  basic_string_view<_CharT> __str,
>+			  _Term_char __term)
>+    {
>+      using _Str_view = basic_string_view<_CharT>;
>+      auto __first = __str.begin();
>+      auto const __last = __str.end();
>+      while (__first != __last)
>+      {
>+	auto __print = __first;
>+	// assume anything outside ASCII is printable
>+	while (__print != __last
>+		 && !__format::__should_escape_ascii(*__print, __term))
>+	  ++__print;
>+
>+	if (__print != __first)
>+	  __out = __format::__write(__out, _Str_view(__first, __print));
>+
>+	if (__print == __last)
>+	  return __out;
>+
>+	__first = __print;
>+	__out = __format::__write_escaped_char(__out, *__first);
>+	++__first;
>+      }
>+      return __out;
>+    }
>+
>+  template<typename _CharT, typename _Out>
>+    _Out
>+    __write_escaped_unicode(_Out __out,
>+			    basic_string_view<_CharT> __str,
>+			    _Term_char __term)
>+    {
>+      using _Str_view = basic_string_view<_CharT>;
>+      using _UChar = make_unsigned_t<_CharT>;
>+      using _Esc = _Escapes<_CharT>;
>+
>+      static constexpr char32_t __replace = U'\uFFFD';
>+      static constexpr _Str_view __replace_rep = []
>+        {
>+	  // N.B. "\uFFFD" is ill-formed if encoding is not unicode.
>+          if constexpr (is_same_v<char, _CharT>)
>+	    return "\xEF\xBF\xBD";
>+	  else
>+	    return L"\xFFFD";
>+        }();
>+
>+      __unicode::_Utf_view<char32_t, _Str_view> __v(std::move(__str));
>+      auto __first = __v.begin();
>+      auto const __last = __v.end();
>+
>+      bool __prev_esc = true;
>+      while (__first != __last)
>+	{
>+	  bool __esc_ascii = false;
>+	  bool __esc_unicode = false;
>+	  bool __esc_replace = false;
>+	  auto __should_escape = [&](auto const& __it)
>+	    {
>+	      if (*__it <= 0x7f)
>+		return __esc_ascii
>+			 = __format::__should_escape_ascii(*__it.base(), __term);
>+	      if (__format::__should_escape_unicode(*__it, __prev_esc))
>+		return __esc_unicode = true;
>+	      if (*__it == __replace)
>+		{
>+		  _Str_view __units(__it.base(), __it._M_units());
>+		  return __esc_replace = (__units != __replace_rep);
>+		}
>+	      return false;
>+	    };
>+
>+	  auto __print = __first;
>+	  while (__print != __last && !__should_escape(__print))
>+	    {
>+	      __prev_esc = false;
>+	      ++__print;
>+	    }
>+
>+	  if (__print != __first)
>+	    __out = __format::__write(__out, _Str_view(__first.base(), __print.base()));
>+
>+	  if (__print == __last)
>+	    return __out;
>+
>+	  __first = __print;
>+	  if (__esc_ascii)
>+	    __out = __format::__write_escaped_char(__out, *__first.base());
>+	  else if (__esc_unicode)
>+	    __out = __format::__write_escape_seq(__out, *__first, _Esc::_S_u());
>+	  else // __esc_replace
>+	    for (_CharT __c : _Str_view(__first.base(), __first._M_units()))
>+	      __out = __format::__write_escape_seq(__out,
>+						   static_cast<_UChar>(__c),
>+						   _Esc::_S_x());
>+	  __prev_esc = true;
>+	  ++__first;
>+
>+	}
>+      return __out;
>+    }
>+
>+  template<typename _CharT, typename _Out>
>+    _Out
>+    __write_escaped(_Out __out,  basic_string_view<_CharT> __str, _Term_char __term)
>+    {
>+      *__out = _Escapes<_CharT>::_S_term(__term);
>+      ++__out;
>+
>+      if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
>+	__out = __format::__write_escaped_unicode(__out, __str, __term);
>+      else if constexpr (is_same_v<char, _CharT>
>+			  && __unicode::__literal_encoding_is_extended_ascii())
>+	__out = __format::__write_escaped_ascii(__out, __str, __term);
>+      else
>+	// TODO Handle non-ascii extended encoding
>+	__out = __format::__write_escaped_ascii(__out, __str, __term);
>+
>+      *__out = _Escapes<_CharT>::_S_term(__term);
>+      return ++__out;
>+    }
>+
>   // A lightweight optional<locale>.
>   struct _Optional_locale
>   {
>@@ -961,7 +1268,7 @@ namespace __format
>
> 	if (*__first == 's')
> 	  ++__first;
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> 	else if (*__first == '?')
> 	  {
> 	    __spec._M_type = _Pres_esc;
>@@ -980,43 +1287,71 @@ namespace __format
> 	format(basic_string_view<_CharT> __s,
> 	       basic_format_context<_Out, _CharT>& __fc) const
> 	{
>-	  if (_M_spec._M_type == _Pres_esc)
>+	  constexpr auto __term = __format::_Term_char::_Tc_quote;
>+	  const auto __write_direct = [&]
> 	    {
>-	      // TODO: C++23 escaped string presentation
>-	    }
>+	      if (_M_spec._M_type == _Pres_esc)
>+		return __format::__write_escaped(__fc.out(), __s, __term);
>+	      else
>+		return __format::__write(__fc.out(), __s);
>+	    };
>
> 	  if (_M_spec._M_width_kind == _WP_none
> 		&& _M_spec._M_prec_kind == _WP_none)
>-	    return __format::__write(__fc.out(), __s);
>+	    return __write_direct();
>
>-	  size_t __estimated_width;
>-	  if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
>-	    {
>-	      if (_M_spec._M_prec_kind != _WP_none)
>-		{
>-		  size_t __prec = _M_spec._M_get_precision(__fc);
>-		  __estimated_width = __unicode::__truncate(__s, __prec);
>-		}
>-	      else
>-		__estimated_width = __unicode::__field_width(__s);
>-	    }
>-	  else
>-	    {
>-	      __s = __s.substr(0, _M_spec._M_get_precision(__fc));
>-	      __estimated_width = __s.size();
>-	    }
>+	  const size_t __prec =
>+	    _M_spec._M_prec_kind != _WP_none
>+	      ? _M_spec._M_get_precision(__fc)
>+	      : basic_string_view<_CharT>::npos;
>
>-	  return __format::__write_padded_as_spec(__s, __estimated_width,
>+	  const size_t __estimated_width = _S_trunc(__s, __prec);
>+	  // N.B. Escaping only increases width
>+	  if (_M_spec._M_get_width(__fc) <= __estimated_width
>+		&& _M_spec._M_prec_kind == _WP_none)
>+	     return __write_direct();
>+
>+	  if (_M_spec._M_type != _Pres_esc)
>+	    return __format::__write_padded_as_spec(__s, __estimated_width,
>+						    __fc, _M_spec);
>+
>+	  __format::_Str_sink<_CharT> __sink;
>+	  __format::_Sink_iter<_CharT> __out(__sink);
>+	  __format::__write_escaped(__out, __s, __term);
>+	  basic_string_view<_CharT> __escaped(__sink.view().data(),
>+					      __sink.view().size());
>+	  const size_t __escaped_width = _S_trunc(__escaped, __prec);
>+	  // N.B. [tab:format.type.string] defines '?' as
>+	  // Copies the escaped string ([format.string.escaped]) to the output,
>+	  // so precision seem to appy to escaped string.
>+	  return __format::__write_padded_as_spec(__escaped, __escaped_width,
> 						  __fc, _M_spec);
> 	}
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void
>       set_debug_format() noexcept
>       { _M_spec._M_type = _Pres_esc; }
> #endif
>
>     private:
>+      static size_t
>+      _S_trunc(basic_string_view<_CharT>& __s, size_t __prec)
>+      {
>+	if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
>+	 {
>+	   if (__prec != basic_string_view<_CharT>::npos)
>+	     return __unicode::__truncate(__s, __prec);
>+	   else
>+	     return __unicode::__field_width(__s);
>+	 }
>+       else
>+	{
>+	  __s = __s.substr(0, __prec);
>+	  return __s.size();
>+	}
>+      }
>+
>       _Spec<_CharT> _M_spec{};
>     };
>
>@@ -1120,7 +1455,7 @@ namespace __format
> 		++__first;
> 	      }
> 	    break;
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> 	  case '?':
> 	    if (__type == _AsChar)
> 	      {
>@@ -1272,7 +1607,7 @@ namespace __format
>       _S_character_width(_CharT __c)
>       {
> 	// N.B. single byte cannot encode charcter of width greater than 1
>-	if constexpr (sizeof(_CharT) > 1u &&
>+	if constexpr (sizeof(_CharT) > 1u &&
> 			__unicode::__literal_encoding_is_unicode<_CharT>())
> 	  return __unicode::__field_width(__c);
> 	else
>@@ -1286,7 +1621,34 @@ namespace __format
> 	{
> 	  return __format::__write_padded_as_spec({&__c, 1u},
> 						  _S_character_width(__c),
>-			                          __fc, _M_spec);
>+						  __fc, _M_spec);
>+	}
>+
>+      template<typename _Out>
>+	typename basic_format_context<_Out, _CharT>::iterator
>+	_M_format_character_escaped(_CharT __c,
>+				   basic_format_context<_Out, _CharT>& __fc) const
>+	{
>+	  using _Esc = _Escapes<_CharT>;
>+	  constexpr auto __term = __format::_Term_char::_Tc_apos;
>+	  const basic_string_view<_CharT> __in(&__c, 1u);
>+	  if (_M_spec._M_get_width(__fc) <= 3u)
>+	    return __format::__write_escaped(__fc.out(), __in, __term);
>+
>+	  _CharT __buf[12];
>+	  __format::_Fixedbuf_sink<_CharT> __sink(__buf);
>+	  __format::_Sink_iter<_CharT> __out(__sink);
>+	  __format::__write_escaped(__out, __in, __term);
>+
>+	  const basic_string_view<_CharT> __escaped = __sink.view();
>+	  size_t __estimated_width;
>+	  if (__escaped[1] == _Esc::_S_bslash()[0]) // escape sequence
>+	    __estimated_width = __escaped.size();
>+	  else
>+	    __estimated_width = 2 + _S_character_width(__c);
>+	  return __format::__write_padded_as_spec(__escaped,
>+						  __estimated_width,
>+						  __fc, _M_spec);
> 	}
>
>       template<typename _Int>
>@@ -1973,15 +2335,12 @@ namespace __format
> 	      || _M_f._M_spec._M_type == __format::_Pres_c)
> 	    return _M_f._M_format_character(__u, __fc);
> 	  else if (_M_f._M_spec._M_type == __format::_Pres_esc)
>-	    {
>-	      // TODO
>-	      return __fc.out();
>-	    }
>+	    return _M_f._M_format_character_escaped(__u, __fc);
> 	  else
> 	    return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc);
> 	}
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void
>       set_debug_format() noexcept
>       { _M_f._M_spec._M_type = __format::_Pres_esc; }
>@@ -2012,15 +2371,12 @@ namespace __format
> 	      || _M_f._M_spec._M_type == __format::_Pres_c)
> 	    return _M_f._M_format_character(__u, __fc);
> 	  else if (_M_f._M_spec._M_type == __format::_Pres_esc)
>-	    {
>-	      // TODO
>-	      return __fc.out();
>-	    }
>+	    return _M_f._M_format_character_escaped(__u, __fc);
> 	  else
> 	    return _M_f.format(static_cast<unsigned char>(__u), __fc);
> 	}
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void
>       set_debug_format() noexcept
>       { _M_f._M_spec._M_type = __format::_Pres_esc; }
>@@ -2050,7 +2406,7 @@ namespace __format
> 	format(_CharT* __u, basic_format_context<_Out, _CharT>& __fc) const
> 	{ return _M_f.format(__u, __fc); }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
> #endif
>
>@@ -2075,7 +2431,7 @@ namespace __format
> 	       basic_format_context<_Out, _CharT>& __fc) const
> 	{ return _M_f.format(__u, __fc); }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
> #endif
>
>@@ -2099,7 +2455,7 @@ namespace __format
> 	       basic_format_context<_Out, _CharT>& __fc) const
> 	{ return _M_f.format({__u, _Nm}, __fc); }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
> #endif
>
>@@ -2123,7 +2479,7 @@ namespace __format
> 	       basic_format_context<_Out, char>& __fc) const
> 	{ return _M_f.format(__u, __fc); }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
> #endif
>
>@@ -2148,7 +2504,7 @@ namespace __format
> 	       basic_format_context<_Out, wchar_t>& __fc) const
> 	{ return _M_f.format(__u, __fc); }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
> #endif
>
>@@ -2173,7 +2529,7 @@ namespace __format
> 	       basic_format_context<_Out, char>& __fc) const
> 	{ return _M_f.format(__u, __fc); }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
> #endif
>
>@@ -2198,7 +2554,7 @@ namespace __format
> 	       basic_format_context<_Out, wchar_t>& __fc) const
> 	{ return _M_f.format(__u, __fc); }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges // C++ >= 23 && HOSTED
>       constexpr void set_debug_format() noexcept { _M_f.set_debug_format(); }
> #endif
>
>@@ -2859,6 +3215,32 @@ namespace __format
>       { return _Sink_iter<_CharT>(*this); }
>     };
>
>+
>+  template<typename _CharT>
>+    class _Fixedbuf_sink final : public _Sink<_CharT>
>+    {
>+      void
>+      _M_overflow() override
>+      {
>+	__glibcxx_assert(false);
>+	this->_M_rewind();
>+      }
>+
>+    public:
>+      [[__gnu__::__always_inline__]]
>+      constexpr explicit
>+      _Fixedbuf_sink(span<_CharT> __buf)
>+	: _Sink<_CharT>(__buf)
>+      { }
>+
>+      constexpr basic_string_view<_CharT>
>+      view() const
>+      {
>+	auto __s = this->_M_used();
>+	return basic_string_view<_CharT>(__s.data(), __s.size());
>+      }
>+    };
>+
>   // A sink with an internal buffer. This is used to implement concrete sinks.
>   template<typename _CharT>
>     class _Buf_sink : public _Sink<_CharT>
>@@ -2993,13 +3375,6 @@ namespace __format
>       }
>     };
>
>-  template<typename _CharT, typename _Alloc = allocator<_CharT>>
>-    using _Str_sink
>-      = _Seq_sink<basic_string<_CharT, char_traits<_CharT>, _Alloc>>;
>-
>-  // template<typename _CharT, typename _Alloc = allocator<_CharT>>
>-    // using _Vec_sink = _Seq_sink<vector<_CharT, _Alloc>>;
>-
>   // A sink that writes to an output iterator.
>   // Writes to a fixed-size buffer and then flushes to the output iterator
>   // when the buffer fills up.
>@@ -3675,17 +4050,17 @@ namespace __format
> 	  return _M_visit([&__vis]<typename _Tp>(_Tp& __val) -> decltype(auto)
> 	    {
> 	      constexpr bool __user_facing = __is_one_of<_Tp,
>-	        monostate, bool, _CharT,
>-	        int, unsigned int, long long int, unsigned long long int,
>-	        float, double, long double,
>-	        const _CharT*, basic_string_view<_CharT>,
>-	        const void*, handle>::value;
>+		monostate, bool, _CharT,
>+		int, unsigned int, long long int, unsigned long long int,
>+		float, double, long double,
>+		const _CharT*, basic_string_view<_CharT>,
>+		const void*, handle>::value;
> 	     if constexpr (__user_facing)
> 	       return std::forward<_Visitor>(__vis)(__val);
> 	     else
> 	       {
>-	         handle __h(__val);
>-	         return std::forward<_Visitor>(__vis)(__h);
>+		 handle __h(__val);
>+		 return std::forward<_Visitor>(__vis)(__h);
> 	       }
> 	   }, __type);
> 	}
>@@ -4781,6 +5156,7 @@ namespace __format
>     : __format::__range_default_formatter<format_kind<_Rg>, _Rg, _CharT>
>     { };
> #endif // C++23 formatting ranges
>+#undef _GLIBCXX_WIDEN
>
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace std
>diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
>index 16f6e86dcee..2586225dd05 100644
>--- a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
>+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc
>@@ -3,7 +3,6 @@
>
> #include <format>
> #include <vector>
>-#include <chrono> // For _Widen
> #include <testsuite_hooks.h>
>
> static_assert(!std::formattable<std::vector<bool>::reference, int>);
>@@ -21,7 +20,7 @@ is_format_string_for(const char* str, Args&&... args)
>   }
> }
>
>-#define WIDEN_(C, S) ::std::chrono::__detail::_Widen<C>(S, L##S)
>+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
> #define WIDEN(S) WIDEN_(_CharT, S)
>
> void
>diff --git a/libstdc++-v3/testsuite/std/format/debug.cc b/libstdc++-v3/testsuite/std/format/debug.cc
>new file mode 100644
>index 00000000000..07cd1e0e349
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/std/format/debug.cc
>@@ -0,0 +1,454 @@
>+// { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32LE -DUNICODE_ENC" }
>+// { dg-do run { target c++23 } }
>+// { dg-add-options no_pch }
>+
>+#include <format>
>+#include <testsuite_hooks.h>
>+
>+std::string
>+fdebug(char t)
>+{ return std::format("{:?}", t); }
>+
>+std::wstring
>+fdebug(wchar_t t)
>+{ return std::format(L"{:?}", t); }
>+
>+std::string
>+fdebug(std::string_view t)
>+{ return std::format("{:?}", t); }
>+
>+std::wstring
>+fdebug(std::wstring_view t)
>+{ return std::format(L"{:?}", t); }
>+
>+
>+#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S)
>+#define WIDEN(S) WIDEN_(_CharT, S)
>+
>+template<typename _CharT>
>+void
>+test_basic_escapes()
>+{
>+  std::basic_string<_CharT> res;
>+
>+  const auto tab = WIDEN("\t");
>+  res = fdebug(tab);
>+  VERIFY( res == WIDEN(R"("\t")") );
>+  res = fdebug(tab[0]);
>+  VERIFY( res == WIDEN(R"('\t')") );
>+
>+  const auto nline = WIDEN("\n");
>+  res = fdebug(nline);
>+  VERIFY( res == WIDEN(R"("\n")") );
>+  res = fdebug(nline[0]);
>+  VERIFY( res == WIDEN(R"('\n')") );
>+
>+  const auto carret = WIDEN("\r");
>+  res = fdebug(carret);
>+  VERIFY( res == WIDEN(R"("\r")") );
>+  res = fdebug(carret[0]);
>+  VERIFY( res == WIDEN(R"('\r')") );
>+
>+  const auto bslash = WIDEN("\\");
>+  res = fdebug(bslash);
>+  VERIFY( res == WIDEN(R"("\\")") );
>+  res = fdebug(bslash[0]);
>+  VERIFY( res == WIDEN(R"('\\')") );
>+
>+  const auto quote = WIDEN("\"");
>+  res = fdebug(quote);
>+  VERIFY( res == WIDEN(R"("\"")") );
>+  res = fdebug(quote[0]);
>+  VERIFY( res == WIDEN(R"('"')") );
>+
>+  const auto apos = WIDEN("\'");
>+  res = fdebug(apos);
>+  VERIFY( res == WIDEN(R"("'")") );
>+  res = fdebug(apos[0]);
>+  VERIFY( res == WIDEN(R"('\'')") );
>+}
>+
>+template<typename _CharT>
>+void
>+test_ascii_escapes()
>+{
>+  std::basic_string<_CharT> res;
>+
>+  const auto in = WIDEN("\x10 abcde\x7f\t0123");
>+  res = fdebug(in);
>+  VERIFY( res == WIDEN(R"("\u{10} abcde\u{7f}\t0123")") );
>+  res = fdebug(in[0]);
>+  VERIFY( res == WIDEN(R"('\u{10}')") );
>+  res = fdebug(in[1]);
>+  VERIFY( res == WIDEN(R"(' ')") );
>+  res = fdebug(in[2]);
>+  VERIFY( res == WIDEN(R"('a')") );
>+}
>+
>+template<typename _CharT>
>+void
>+test_extended_ascii()
>+{
>+  std::basic_string<_CharT> res;
>+
>+  const auto in = WIDEN("Åëÿ");
>+  res = fdebug(in);
>+  VERIFY( res == WIDEN(R"("Åëÿ")") );
>+
>+  static constexpr bool __test_characters
>+#if UNICODE_ENC
>+    = sizeof(_CharT) >= 2;
>+#else // ISO8859-1
>+    = true;
>+#endif // UNICODE_ENC
>+
>+  if constexpr (__test_characters)
>+  {
>+    res = fdebug(in[0]);
>+    VERIFY( res == WIDEN(R"('Å')") );
>+    res = fdebug(in[1]);
>+    VERIFY( res == WIDEN(R"('ë')") );
>+    res = fdebug(in[2]);
>+    VERIFY( res == WIDEN(R"('ÿ')") );
>+  }
>+}
>+
>+#if UNICODE_ENC
>+template<typename _CharT>
>+void
>+test_unicode_escapes()
>+{
>+  std::basic_string<_CharT> res;
>+
>+  const auto in = WIDEN(
>+    "\u008a"     // Cc, Control,             Line Tabulation Set,
>+    "\u00ad"     // Cf, Format,              Soft Hyphen
>+    "\u1d3d"     // Lm, Modifier letter,     Modifier Letter Capital Ou
>+    "\u00a0"     // Zs, Space Separator,     No-Break Space (NBSP)
>+    "\u2029"     // Zp, Paragraph Separator, Paragraph Separator
>+    "\U0001f984" // So, Other Symbol,        Unicorn Face
>+  );
>+  const auto out = WIDEN("\""
>+   R"(\u{8a})"
>+   R"(\u{ad})"
>+   "\u1d3d"
>+   R"(\u{a0})"
>+   R"(\u{2029})"
>+   "\U0001f984"
>+  "\"");
>+
>+  res = fdebug(in);
>+  VERIFY( res == out );
>+
>+  if constexpr (sizeof(_CharT) >= 2)
>+  {
>+    res = fdebug(in[0]);
>+    VERIFY( res == WIDEN(R"('\u{8a}')") );
>+    res = fdebug(in[1]);
>+    VERIFY( res == WIDEN(R"('\u{ad}')") );
>+    res = fdebug(in[2]);
>+    VERIFY( res == WIDEN("'\u1d3d'") );
>+    res = fdebug(in[3]);
>+    VERIFY( res == WIDEN(R"('\u{a0}')") );
>+    res = fdebug(in[4]);
>+    VERIFY( res == WIDEN(R"('\u{2029}')") );
>+  }
>+
>+  if constexpr (sizeof(_CharT) >= 4)
>+  {
>+    res = fdebug(in[5]);
>+    VERIFY( res == WIDEN("'\U0001f984'") );
>+  }
>+}
>+
>+template<typename _CharT>
>+void
>+test_grapheme_extend()
>+{
>+  std::basic_string<_CharT> res;
>+
>+  const auto vin = WIDEN("o\u0302\u0323");
>+  res = fdebug(vin);
>+  VERIFY( res == WIDEN("\"o\u0302\u0323\"") );
>+
>+  std::basic_string_view<_CharT> in = WIDEN("\t\u0302\u0323");
>+  res = fdebug(in);
>+  VERIFY( res == WIDEN(R"("\t\u{302}\u{323}")") );
>+
>+  res = fdebug(in.substr(1));
>+  VERIFY( res == WIDEN(R"("\u{302}\u{323}")") );
>+
>+  if constexpr (sizeof(_CharT) >= 2)
>+  {
>+    res = fdebug(in[1]);
>+    VERIFY( res == WIDEN(R"('\u{302}')") );
>+  }
>+}
>+
>+template<typename _CharT>
>+void
>+test_replacement_char()
>+{
>+  std::basic_string<_CharT> repl = WIDEN("\uFFFD");
>+  std::basic_string<_CharT> res = fdebug(repl);
>+  VERIFY( res == WIDEN("\"\uFFFD\"") );
>+
>+  repl = WIDEN("\uFFFD\uFFFD");
>+  res = fdebug(repl);
>+  VERIFY( res == WIDEN("\"\uFFFD\uFFFD\"") );
>+}
>+
>+void
>+test_ill_formed_utf8_seq()
>+{
>+  std::string_view seq = "\xf0\x9f\xa6\x84"; //  \U0001F984
>+  std::string res;
>+
>+  res = fdebug(seq);
>+  VERIFY( res == "\"\U0001F984\"" );
>+
>+  res = fdebug(seq.substr(1));
>+  VERIFY( res == R"("\x{9f}\x{a6}\x{84}")" );
>+
>+  res = fdebug(seq.substr(2));
>+  VERIFY( res == R"("\x{a6}\x{84}")" );
>+
>+  res = fdebug(seq[0]);
>+  VERIFY( res == R"('\x{f0}')" );
>+  res = fdebug(seq.substr(0, 1));
>+  VERIFY( res == R"("\x{f0}")" );
>+
>+  res = fdebug(seq[1]);
>+  VERIFY( res == R"('\x{9f}')" );
>+  res = fdebug(seq.substr(1, 1));
>+  VERIFY( res == R"("\x{9f}")" );
>+
>+  res = fdebug(seq[2]);
>+  VERIFY( res == R"('\x{a6}')" );
>+  res = fdebug(seq.substr(2, 1));
>+  VERIFY( res == R"("\x{a6}")" );
>+
>+  res = fdebug(seq[3]);
>+  VERIFY( res == R"('\x{84}')" );
>+  res = fdebug(seq.substr(3, 1));
>+  VERIFY( res == R"("\x{84}")" );
>+}
>+
>+void
>+test_ill_formed_utf32()
>+{
>+  std::wstring res;
>+
>+  wchar_t ic1 = static_cast<wchar_t>(0xff'ffff);
>+  res = fdebug(ic1);
>+  VERIFY( res == LR"('\x{ffffff}')" );
>+
>+  std::wstring is1(1, ic1);
>+  res = fdebug(is1);
>+  VERIFY( res == LR"("\x{ffffff}")" );
>+
>+  wchar_t ic2 = static_cast<wchar_t>(0xffff'ffff);
>+  res = fdebug(ic2);
>+  VERIFY( res == LR"('\x{ffffffff}')" );
>+
>+  std::wstring is2(1, ic2);
>+  res = fdebug(is2);
>+  VERIFY( res == LR"("\x{ffffffff}")" );
>+}
>+#endif // UNICODE_ENC
>+
>+template<typename _CharT>
>+void
>+test_fill()
>+{
>+  std::basic_string<_CharT> res;
>+
>+  std::basic_string_view<_CharT> in = WIDEN("a\t\x10\u00ad");
>+  res = std::format(WIDEN("{:10?}"), in.substr(0, 1));
>+  VERIFY( res == WIDEN(R"("a"       )") );
>+
>+  res = std::format(WIDEN("{:->10?}"), in.substr(1, 1));
>+  VERIFY( res == WIDEN(R"(------"\t")") );
>+
>+  res = std::format(WIDEN("{:+<10?}"), in.substr(2, 1));
>+  VERIFY( res == WIDEN(R"("\u{10}"++)") );
>+
>+
>+  res = std::format(WIDEN("{:10?}"), in[0]);
>+  VERIFY( res == WIDEN(R"('a'       )") );
>+
>+  res = std::format(WIDEN("{:->10?}"), in[1]);
>+  VERIFY( res == WIDEN(R"(------'\t')") );
>+
>+  res = std::format(WIDEN("{:+<10?}"), in[2]);
>+  VERIFY( res == WIDEN(R"('\u{10}'++)") );
>+
>+#if UNICODE_ENC
>+  res = std::format(WIDEN("{:=^10?}"), in.substr(3));
>+  VERIFY( res == WIDEN(R"(="\u{ad}"=)") );
>+
>+  // width is 2
>+  std::basic_string_view<_CharT> in2 = WIDEN("\u1100");
>+  res = std::format(WIDEN("{:*^10?}"), in2);
>+  VERIFY( res == WIDEN("***\"\u1100\"***") );
>+
>+  if constexpr (sizeof(_CharT) >= 2)
>+  {
>+    res = std::format(WIDEN("{:=^10?}"), in[3]);
>+    VERIFY( res == WIDEN(R"(='\u{ad}'=)") );
>+
>+    res = std::format(WIDEN("{:*^10?}"), in2[0]);
>+    VERIFY( res == WIDEN("***'\u1100'***") );
>+  }
>+#endif // UNICODE_ENC
>+}
>+
>+template<typename _CharT>
>+void
>+test_prec()
>+{
>+  std::basic_string<_CharT> res;
>+  // with ? escpaed presentation is copied to ouput, same as source
>+
>+  std::basic_string_view<_CharT> in = WIDEN("a\t\x10\u00ad");
>+  res = std::format(WIDEN("{:.2?}"), in.substr(0, 1));
>+  VERIFY( res == WIDEN(R"("a)") );
>+
>+  res = std::format(WIDEN("{:.4?}"), in.substr(1, 1));
>+  VERIFY( res == WIDEN(R"("\t")") );
>+
>+  res = std::format(WIDEN("{:.5?}"), in.substr(2, 1));
>+  VERIFY( res == WIDEN(R"("\u{1)") );
>+
>+#if UNICODE_ENC
>+  res = std::format(WIDEN("{:.10?}"), in.substr(3));
>+  VERIFY( res == WIDEN(R"("\u{ad}")") );
>+
>+  std::basic_string_view<_CharT> in2 = WIDEN("\u1100");
>+  res = std::format(WIDEN("{:.3?}"), in2);
>+  VERIFY( res == WIDEN("\"\u1100") );
>+#endif // UNICODE_ENC
>+}
>+
>+void test_char_as_wchar()
>+{
>+  std::wstring res;
>+
>+  res = std::format(L"{:?}", 'a');
>+  VERIFY( res == LR"('a')" );
>+
>+  res = std::format(L"{:?}", '\t');
>+  VERIFY( res == LR"('\t')" );
>+
>+  res = std::format(L"{:+<10?}", '\x10');
>+  VERIFY( res == LR"('\u{10}'++)" );
>+}
>+
>+template<typename T>
>+struct DebugWrapper
>+{
>+  T val;
>+};
>+
>+template<typename T, typename CharT>
>+struct std::formatter<DebugWrapper<T>, CharT>
>+{
>+  constexpr std::basic_format_parse_context<CharT>::iterator
>+  parse(std::basic_format_parse_context<CharT>& pc)
>+  {
>+    auto out = under.parse(pc);
>+    under.set_debug_format();
>+    return out;
>+  }
>+
>+  template<typename Out>
>+  Out format(DebugWrapper<T> const& t,
>+	     std::basic_format_context<Out, CharT>& fc) const
>+  { return under.format(t.val, fc); }
>+
>+private:
>+  std::formatter<T, CharT> under;
>+};
>+
>+template<typename _CharT, typename StrT>
>+void
>+test_formatter_str()
>+{
>+  _CharT buf[]{ 'a', 'b', 'c', 0 };
>+  DebugWrapper<StrT> in{ buf };
>+  std::basic_string<_CharT> res = std::format(WIDEN("{:?}"), in );
>+  VERIFY( res == WIDEN(R"("abc")") );
>+}
>+
>+template<typename _CharT>
>+void
>+test_formatter_arr()
>+{
>+  std::basic_string<_CharT> res;
>+
>+  DebugWrapper<_CharT[3]> in3{ 'a', 'b', 'c' };
>+  res = std::format(WIDEN("{:?}"), in3 );
>+  VERIFY( res == WIDEN(R"("abc")") );
>+
>+  // We print all characters, including null-terminator
>+  DebugWrapper<_CharT[4]> in4{ 'a', 'b', 'c', 0 };
>+  res = std::format(WIDEN("{:?}"), in4 );
>+  VERIFY( res == WIDEN(R"("abc\u{0}")") );
>+}
>+
>+template<typename _CharT, typename SrcT>
>+void
>+test_formatter_char()
>+{
>+  DebugWrapper<SrcT> in{ 'a' };
>+  std::basic_string<_CharT> res = std::format(WIDEN("{:?}"), in);
>+  VERIFY( res == WIDEN(R"('a')") );
>+}
>+
>+template<typename CharT>
>+void
>+test_formatters()
>+{
>+  test_formatter_char<CharT, CharT>();
>+  test_formatter_str<CharT, CharT*>();
>+  test_formatter_str<CharT, const CharT*>();
>+  test_formatter_str<CharT, std::basic_string<CharT>>();
>+  test_formatter_str<CharT, std::basic_string_view<CharT>>();
>+  test_formatter_arr<CharT>();
>+}
>+
>+void
>+test_formatters_c()
>+{
>+  test_formatters<char>();
>+  test_formatters<wchar_t>();
>+  test_formatter_char<wchar_t, char>();
>+}
>+
>+int main()
>+{
>+  test_basic_escapes<char>();
>+  test_basic_escapes<wchar_t>();
>+  test_ascii_escapes<char>();
>+  test_ascii_escapes<wchar_t>();
>+  test_extended_ascii<char>();
>+  test_extended_ascii<wchar_t>();
>+
>+#if UNICODE_ENC
>+  test_unicode_escapes<char>();
>+  test_unicode_escapes<wchar_t>();
>+  test_grapheme_extend<char>();
>+  test_grapheme_extend<wchar_t>();
>+  test_replacement_char<char>();
>+  test_replacement_char<wchar_t>();
>+  test_ill_formed_utf8_seq();
>+  test_ill_formed_utf32();
>+#endif // UNICODE_ENC
>+
>+  test_fill<char>();
>+  test_fill<wchar_t>();
>+  test_prec<char>();
>+  test_prec<wchar_t>();
>+
>+  test_formatters_c();
>+}
>diff --git a/libstdc++-v3/testsuite/std/format/debug_nounicode.cc b/libstdc++-v3/testsuite/std/format/debug_nounicode.cc
>new file mode 100644
>index 00000000000..5c03171d71a
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/std/format/debug_nounicode.cc
>@@ -0,0 +1,5 @@
>+// { dg-options "-fexec-charset=ISO8859-1 -fwide-exec-charset=UTF-32LE" }
>+// { dg-do run { target c++23 } }
>+// { dg-add-options no_pch }
>+
>+#include "debug.cc"
>diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
>index b5dd7cdba78..b338ac7b762 100644
>--- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc
>+++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc
>@@ -108,7 +108,7 @@ is_std_format_spec_for(std::string_view spec)
>   }
> }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges
> constexpr bool escaped_strings_supported = true;
> #else
> constexpr bool escaped_strings_supported = false;
>diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc
>index ee987a15ec3..76614d4bc3e 100644
>--- a/libstdc++-v3/testsuite/std/format/string.cc
>+++ b/libstdc++-v3/testsuite/std/format/string.cc
>@@ -62,7 +62,7 @@ test_indexing()
>   VERIFY( ! is_format_string_for("{} {0}", 1) );
> }
>
>-#if __cpp_lib_format_ranges
>+#if __glibcxx_format_ranges
> constexpr bool escaped_strings_supported = true;
> #else
> constexpr bool escaped_strings_supported = false;
>-- 
>2.49.0
>
>



More information about the Libstdc++ mailing list