On Thu, Apr 10, 2025 at 6:39 PM Jonathan Wakely <jwak...@redhat.com> wrote:
> 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. > The are already in master, as I merged them with formatter for vector<bool>::reference: https://gcc.gnu.org/cgit/gcc/commit/?id=84d668b0ca67c5d3fe6430f101d61e60aa796a81 > > > * 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 > > > > > >