[PATCH v4] libstdc++: Cleanup and stabilize format _Spec<_CharT> and _Pres_type.
Jonathan Wakely
jwakely@redhat.com
Tue May 20 09:41:45 GMT 2025
On 20/05/25 09:35 +0200, Tomasz Kamiński wrote:
>These patch makes following changes to _Pres_type values:
> * _Pres_esc is replaced with separate _M_debug flag.
> * _Pres_s, _Pres_p do not overlap with _Pres_none.
> * hexadecimal presentation use same values for pointer, integer
> and floating point types.
>
>The members of _Spec<_CharT> are rearanged so the class contains 8 bits
s/rearanged/rearranged/
>of reserved for future use (_M_reserved) and 8 bits of tail padding.
s/of reserved/reserved/
>Derived classes (like _ChronoSpec<_CharT>) can reuse the storage for initial
>members. We also add _SpecBase as the base class for _Spec<_CharT> to make
>it non-C++98 POD, which allows tail padding to be reused on Itanium ABI.
>
>Finally, the format enumerators are defined as enum class with unsigned
>char as underlying type, followed by using enum to bring names in scope.
>_Term_char names are adjusted for consistency, and enumerator values are
>changed so it can fit in smaller bitfields.
>
>The '?' is changed to separate _M_debug flag, to allow debug format to be
>independent from the presentation type, and applied to multiple presentation
>types. For example it could be used to trigger memberwise or reflection based
>formatting.
>
>The _M_format_character and _M_format_character_escaped functions are merged
>to single function that handle normal and debug presentation. In particular
>this would allow future support for '?c' for printing integer types as escaped
>character. _S_character_width is also folded in the merged function.
>
>Decoupling _Pres_s value from _Pres_none, allows it to be used for string
>presentation for range formatting, and removes the need for separate _Pres_seq
>and _Pres_str. This does not affect formatting of bool as __formatter_int::_M_parse
>overrides default value of _M_type. And with separation of the _M_debug flag,
>__formatter_str::format behavior is now agnostic to _M_type value.
>
>The values for integer presentation types, are arranged so textual presentations
>(_Prec_s, _Pres_c) are grouped together. For consistency floating point
>hexadecimal presentation uses the same values as integer ones.
>
>New _Pres_p and setting for _M_alt enables using some spec to configure formatting
>of uintptr_t with __formatter_int, and const void* with __formatter_ptr.
>Differentiating it from _Pres_none would allow future of formatter<T*, _CharT>
>that would require explicit presentation type to be specified. This would allow
>std::vector<T*> to be formatter directly with '{::p}' format spec.
s/formatter/formatted/
>The constructors for __formatter_int and _formatter_ptr from _Spec<_CharT>,
s/_formatter_ptr/__formatter_ptr/
>now also set default presentation modes, as format functions expects them.
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/chrono_io.h (_ChronoSpec<_CharT>::_M_locale_specific):
> Declare as bit filed in tail-padding..
s/filed/field/
> * include/bits/formatfwd.h (__format::_Align): Defined as enum class
> and add using enum.
> * include/std/format (__format::_Pres_type, __format::_Sign)
> (__format::_WidthPrec, __format::_Arg_t): Defined as enum class and
> add using enum.
> (_Pres_type::_Pres_esc): Replace with _Pres_max.
> (_Pres_type::_Pres_seq, _Pres_type::_Pres_str): Remove.
> (__format::_Pres_type): Updated values of enumerators as described above.
This line is more than 80 columns, please add a line break.
Please adjust the rest of the ChangeLog to keep to around 74 chars
where possible.
> (_Spec<_CharT>): Rearranged members to have 8 bits of tail-padding.
> (_Spec<_CharT>::_M_debug): Defined.
> (_Spec<_CharT>::_M_reserved): Extended to 8 bits and moved at the end.
> (_Spec<_CharT>::_M_reserved2): Removed.
> (_Spec<_CharT>::_M_parse_fill_and_align, _Spec<_CharT>::_M_parse_sign)
> (__format::__write_padded_as_spec): Adjusted default value checks.
> (__format::_Term_char): Add using enum and adjust enumertors.
> (__Escapes::_S_term): Adjusted for _Term_char values.
> (__format::__should_escape_ascii): Adjusted _Term_char uses.
> (__foramt::__write_escaped): Adjusted for _Term_char.
> (__formatter_str<_CharT>::parse): Set _Pres_s if specifed and _M_debug
> instead of _Pres_esc.
> (__formatter_str<_CharT>::set_debug_format): Set _M_debug instead of
> _Pres_esc.
> (__formatter_str<_CharT>::format, __formatter_str<_CharT>::_M_format_range):
> Check _M_debug instead of _Prec_esc.
> (__formatter_str<_CharT>::_M_format_escaped): Adjusted _Term_char uses.
> (__formatter_int<_CharT>::__formatter_int(_Spec<_CharT>)): Set _Pres_d if
> default presentation type is not set.
> (__formatter_int<_CharT>::_M_parse): Adjusted default value checks.
> (__formatter_int<_CharT>::_M_do_parse): Set _M_debug instead of _Pres_esc.
> (__formatter_int<_CharT>::_M_format_character): Handle escaped presentation.
> (__formatter_int<_CharT>::_M_format_character_escaped)
> (__formatter_int<_CharT>::_S_character_width): Merged into _M_format_character.
> (__formatter_ptr<_CharT>::__formatter_ptr(_Spec<_CharT>)): Set _Pres_p if default
> presentation type is not set.
> (__formatter_ptr<_CharT>::parse): Add default __type parameter, store _Pres_p,
> and handle _M_alt to be consistent with meaning for integers.
> (__foramtter_ptr<_CharT>::_M_set_default): Define.
> (__format::__pack_arg_types, std::basic_format_args): Add necessary casts.
> (formatter<_CharT, _CharT>::set_debug_format)
> (formatter<char, wchar_t>::set_debug_format): Set _M_debug instead of _Pres_esc.
> (formatter<_CharT, _CharT>::format, formatter<char, wchar_t>::format):
> Simplify calls to _M_format_character.
> (range_formatter<_Rg, _CharT>::parse): Replace _Pres_str with _Pres_s
> and set _M_debug instead of _Pres_esc.
> (range_formatter<_Rg, _CharT>::format): Replace _Pres_str with _Pres_s.
>---
>Adjusted values for the _Term_char so they can now fit in 2 bits bitfield.
>Handle _Term_non.
>I think the commit message is now longer than the diff.
:-)
>OK for trunk?
OK, thanks.
(There are two more spelling typos noted below ...)
> libstdc++-v3/include/bits/chrono_io.h | 18 +-
> libstdc++-v3/include/bits/formatfwd.h | 3 +-
> libstdc++-v3/include/std/format | 257 ++++++++++++++------------
> 3 files changed, 145 insertions(+), 133 deletions(-)
>
>diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
>index ace8b9f2629..991142fd83f 100644
>--- a/libstdc++-v3/include/bits/chrono_io.h
>+++ b/libstdc++-v3/include/bits/chrono_io.h
>@@ -207,21 +207,15 @@ namespace __format
> template<typename _CharT>
> struct _ChronoSpec : _Spec<_CharT>
> {
>- basic_string_view<_CharT> _M_chrono_specs;
>-
>- // Use one of the reserved bits in __format::_Spec<C>.
>+ // Placed in tail-padding of __format::_Spec<C>.
> // This indicates that a locale-dependent conversion specifier such as
> // %a is used in the chrono-specs. This is not the same as the
> // _Spec<C>::_M_localized member which indicates that "L" was present
> // in the format-spec, e.g. "{:L%a}" is localized and locale-specific,
> // but "{:L}" is only localized and "{:%a}" is only locale-specific.
>- constexpr bool
>- _M_locale_specific() const noexcept
>- { return this->_M_reserved; }
>+ unsigned _M_locale_specific : 1;
>
>- constexpr void
>- _M_locale_specific(bool __b) noexcept
>- { this->_M_reserved = __b; }
>+ basic_string_view<_CharT> _M_chrono_specs;
> };
>
> // Represents the information provided by a chrono type.
>@@ -488,7 +482,7 @@ namespace __format
> _M_spec = __spec;
> _M_spec._M_chrono_specs
> = __string_view(__chrono_specs, __first - __chrono_specs);
>- _M_spec._M_locale_specific(__locale_specific);
>+ _M_spec._M_locale_specific = __locale_specific;
>
> return __first;
> }
>@@ -512,7 +506,7 @@ namespace __format
> // of chrono types is underspecified
> if constexpr (is_same_v<_CharT, char>)
> if constexpr (__unicode::__literal_encoding_is_utf8())
>- if (_M_spec._M_localized && _M_spec._M_locale_specific())
>+ if (_M_spec._M_localized && _M_spec._M_locale_specific)
> {
> extern locale __with_encoding_conversion(const locale&);
>
>@@ -806,7 +800,7 @@ namespace __format
> // of chrono types is underspecified
> if constexpr (is_same_v<_CharT, char>)
> if constexpr (__unicode::__literal_encoding_is_utf8())
>- if (_M_spec._M_localized && _M_spec._M_locale_specific()
>+ if (_M_spec._M_localized && _M_spec._M_locale_specific
> && __loc != locale::classic())
> {
> extern string_view
>diff --git a/libstdc++-v3/include/bits/formatfwd.h b/libstdc++-v3/include/bits/formatfwd.h
>index 3fa01ad1eab..777e6290f74 100644
>--- a/libstdc++-v3/include/bits/formatfwd.h
>+++ b/libstdc++-v3/include/bits/formatfwd.h
>@@ -71,12 +71,13 @@ namespace __format
> concept __char = same_as<_CharT, char>;
> #endif
>
>- enum _Align {
>+ enum class _Align : unsigned char {
> _Align_default,
> _Align_left,
> _Align_right,
> _Align_centre,
> };
>+ using enum _Align;
>
> template<typename _CharT> struct _Spec;
>
>diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
>index b1823db83bc..c0a10a38152 100644
>--- a/libstdc++-v3/include/std/format
>+++ b/libstdc++-v3/include/std/format
>@@ -472,30 +472,33 @@ namespace __format
> return {0, nullptr};
> }
>
>- enum _Pres_type {
>+ enum class _Pres_type : unsigned char {
> _Pres_none = 0, // Default type (not valid for integer presentation types).
>+ _Pres_s = 1, // For strings, bool, ranges
> // Presentation types for integral types (including bool and charT).
>- _Pres_d = 1, _Pres_b, _Pres_B, _Pres_o, _Pres_x, _Pres_X, _Pres_c,
>- // Presentation types for floating-point types.
>- _Pres_a = 1, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F, _Pres_g, _Pres_G,
>- _Pres_p = 0, _Pres_P, // For pointers.
>- _Pres_s = 0, // For strings, bool
>- _Pres_seq = 0, _Pres_str, // For ranges
>- _Pres_esc = 0xf, // For strings, charT and ranges
This /* s */ confused me more than it helped me, but maybe I'm just
being slow this morning.
>+ /* s */ _Pres_c = 2, _Pres_x, _Pres_X, _Pres_d, _Pres_o, _Pres_b, _Pres_B,
>+ // Presentation types for floating-point types
>+ _Pres_g = 1, _Pres_G, _Pres_a, _Pres_A, _Pres_e, _Pres_E, _Pres_f, _Pres_F,
>+ // For pointers, the value are same as hexadecimal presentations for integers
>+ _Pres_p = _Pres_x, _Pres_P = _Pres_X,
>+ _Pres_max = 0xf,
> };
>+ using enum _Pres_type;
>
>- enum _Sign {
>+ enum class _Sign : unsigned char {
> _Sign_default,
> _Sign_plus,
> _Sign_minus, // XXX does this need to be distinct from _Sign_default?
> _Sign_space,
> };
>+ using enum _Sign;
>
>- enum _WidthPrec {
>+ enum _WidthPrec : unsigned char {
> _WP_none, // No width/prec specified.
> _WP_value, // Fixed width/prec specified.
> _WP_from_arg // Use a formatting argument for width/prec.
> };
>+ using enum _WidthPrec;
>
> template<typename _Context>
> size_t
>@@ -507,9 +510,17 @@ namespace __format
> constexpr bool __is_xdigit(char __c)
> { return std::__detail::__from_chars_alnum_to_val(__c) < 16; }
>
>+ // Used to make _Spec a non-C++98 POD, so the tail-padding is used.
>+ // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#pod
>+ struct _SpecBase
>+ { };
>+
> template<typename _CharT>
>- struct _Spec
>+ struct _Spec : _SpecBase
> {
>+ unsigned short _M_width;
>+ unsigned short _M_prec;
>+ char32_t _M_fill = ' ';
> _Align _M_align : 2;
> _Sign _M_sign : 2;
> unsigned _M_alt : 1;
>@@ -517,12 +528,11 @@ namespace __format
> unsigned _M_zero_fill : 1;
> _WidthPrec _M_width_kind : 2;
> _WidthPrec _M_prec_kind : 2;
>+ unsigned _M_debug : 1;
> _Pres_type _M_type : 4;
>- unsigned _M_reserved : 1;
>- unsigned _M_reserved2 : 16;
>- unsigned short _M_width;
>- unsigned short _M_prec;
>- char32_t _M_fill = ' ';
>+ unsigned _M_reserved : 8;
>+ // This class has 8bits of tail padding, that can be used by
>+ // derived classes.
>
> using iterator = typename basic_string_view<_CharT>::iterator;
>
>@@ -562,7 +572,7 @@ namespace __format
> char32_t __c = *__beg++;
> if (__is_scalar_value(__c))
> if (auto __next = __beg.base(); __next != __last)
>- if (_Align __align = _S_align(*__next))
>+ if (_Align __align = _S_align(*__next); __align != _Align_default)
> {
> _M_fill = __c;
> _M_align = __align;
>@@ -571,14 +581,14 @@ namespace __format
> }
> }
> else if (__last - __first >= 2)
>- if (_Align __align = _S_align(__first[1]))
>+ if (_Align __align = _S_align(__first[1]); __align != _Align_default)
> {
> _M_fill = *__first;
> _M_align = __align;
> return __first + 2;
> }
>
>- if (_Align __align = _S_align(__first[0]))
>+ if (_Align __align = _S_align(__first[0]); __align != _Align_default)
> {
> _M_fill = ' ';
> _M_align = __align;
>@@ -603,7 +613,7 @@ namespace __format
> constexpr iterator
> _M_parse_sign(iterator __first, iterator) noexcept
> {
>- if (_Sign __sign = _S_sign(*__first))
>+ if (_Sign __sign = _S_sign(*__first); __sign != _Sign_default)
> {
> _M_sign = __sign;
> return __first + 1;
>@@ -871,7 +881,7 @@ namespace __format
>
> const size_t __nfill = __width - __estimated_width;
>
>- if (__spec._M_align)
>+ if (__spec._M_align != _Align_default)
> __align = __spec._M_align;
>
> return __format::__write_padded(__fc.out(), __str, __align, __nfill,
>@@ -896,12 +906,12 @@ namespace __format
> }
> }
>
>-
>- // Values are indices into _Escapes::all.
> enum class _Term_char : unsigned char {
>- _Tc_quote = 12,
>- _Tc_apos = 15
>+ _Term_none,
>+ _Term_quote,
>+ _Term_apos,
> };
>+ using enum _Term_char;
>
> template<typename _CharT>
> struct _Escapes
>@@ -912,10 +922,6 @@ namespace __format
> _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); }
>@@ -947,6 +953,21 @@ namespace __format
> static consteval
> _Str_view _S_x()
> { return _S_all().substr(20, 2); }
>+
>+ static constexpr
>+ _Str_view _S_term(_Term_char __term)
>+ {
>+ switch (__term)
>+ {
>+ case _Term_none:
>+ return _Str_view();
>+ case _Term_quote:
>+ return _S_quote().substr(0, 1);
>+ case _Term_apos:
>+ return _S_apos().substr(0, 1);
>+ }
>+ __builtin_unreachable();
>+ }
> };
>
> template<typename _CharT>
>@@ -991,9 +1012,9 @@ namespace __format
> case _Esc::_S_bslash()[0]:
> return true;
> case _Esc::_S_quote()[0]:
>- return __term == _Term_char::_Tc_quote;
>+ return __term == _Term_quote;
> case _Esc::_S_apos()[0]:
>- return __term == _Term_char::_Tc_apos;
>+ return __term == _Term_apos;
> default:
> return (__c >= 0 && __c < 0x20) || __c == 0x7f;
> };
>@@ -1062,9 +1083,8 @@ namespace __format
> 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());
>+ return __format::__write_escape_seq(
>+ __out, static_cast<_UChar>(__c), _Esc::_S_u());
> }
> }
>
>@@ -1177,8 +1197,7 @@ namespace __format
> _Out
> __write_escaped(_Out __out, basic_string_view<_CharT> __str, _Term_char __term)
> {
>- *__out = _Escapes<_CharT>::_S_term(__term);
>- ++__out;
>+ __out = __format::__write(__out, _Escapes<_CharT>::_S_term(__term));
>
> if constexpr (__unicode::__literal_encoding_is_unicode<_CharT>())
> __out = __format::__write_escaped_unicode(__out, __str, __term);
>@@ -1189,8 +1208,7 @@ namespace __format
> // TODO Handle non-ascii extended encoding
> __out = __format::__write_escaped_ascii(__out, __str, __term);
>
>- *__out = _Escapes<_CharT>::_S_term(__term);
>- return ++__out;
>+ return __format::__write(__out, _Escapes<_CharT>::_S_term(__term));
> }
>
> // A lightweight optional<locale>.
>@@ -1312,11 +1330,14 @@ namespace __format
> return __first;
>
> if (*__first == 's')
>- ++__first;
>+ {
>+ __spec._M_type = _Pres_s;
>+ ++__first;
>+ }
> #if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> else if (*__first == '?')
> {
>- __spec._M_type = _Pres_esc;
>+ __spec._M_debug = true;
> ++__first;
> }
> #endif
>@@ -1332,7 +1353,7 @@ namespace __format
> format(basic_string_view<_CharT> __s,
> basic_format_context<_Out, _CharT>& __fc) const
> {
>- if (_M_spec._M_type == _Pres_esc)
>+ if (_M_spec._M_debug)
> return _M_format_escaped(__s, __fc);
>
> if (_M_spec._M_width_kind == _WP_none
>@@ -1349,22 +1370,21 @@ namespace __format
> _M_format_escaped(basic_string_view<_CharT> __s,
> basic_format_context<_Out, _CharT>& __fc) const
> {
>- constexpr auto __term = __format::_Term_char::_Tc_quote;
> const size_t __padwidth = _M_spec._M_get_width(__fc);
> if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none)
>- return __format::__write_escaped(__fc.out(), __s, __term);
>+ return __format::__write_escaped(__fc.out(), __s, _Term_quote);
>
> const size_t __maxwidth = _M_spec._M_get_precision(__fc);
> const size_t __width = __truncate(__s, __maxwidth);
> // N.B. Escaping only increases width
> if (__padwidth <= __width && _M_spec._M_prec_kind == _WP_none)
>- return __format::__write_escaped(__fc.out(), __s, __term);
>+ return __format::__write_escaped(__fc.out(), __s, _Term_quote);
>
> // 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.
> _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth, __maxwidth);
>- __format::__write_escaped(__sink.out(), __s, __term);
>+ __format::__write_escaped(__sink.out(), __s, _Term_quote);
> return __sink._M_finish(_M_spec._M_align, _M_spec._M_fill);
> }
>
>@@ -1388,7 +1408,7 @@ namespace __format
> size_t(ranges::distance(__rg)));
> return format(__str, __fc);
> }
>- else if (_M_spec._M_type != _Pres_esc)
>+ else if (!_M_spec._M_debug)
> {
> const size_t __padwidth = _M_spec._M_get_width(__fc);
> if (__padwidth == 0 && _M_spec._M_prec_kind == _WP_none)
>@@ -1441,7 +1461,7 @@ namespace __format
>
> constexpr void
> set_debug_format() noexcept
>- { _M_spec._M_type = _Pres_esc; }
>+ { _M_spec._M_debug = true; }
> #endif
>
> private:
>@@ -1462,7 +1482,10 @@ namespace __format
> constexpr
> __formatter_int(_Spec<_CharT> __spec) noexcept
> : _M_spec(__spec)
>- { }
>+ {
>+ if (_M_spec._M_type == _Pres_none)
>+ _M_spec._M_type = _Pres_d;
>+ }
>
> constexpr typename basic_format_parse_context<_CharT>::iterator
> _M_do_parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type)
>@@ -1551,7 +1574,7 @@ namespace __format
> case 's':
> if (__type == _AsBool)
> {
>- __spec._M_type = _Pres_s; // same value (and meaning) as "none"
>+ __spec._M_type = _Pres_s; // same meaning as "none" for bool
> ++__first;
> }
> break;
>@@ -1559,7 +1582,7 @@ namespace __format
> case '?':
> if (__type == _AsChar)
> {
>- __spec._M_type = _Pres_esc;
>+ __spec._M_debug = true;
> ++__first;
> }
> #endif
>@@ -1580,7 +1603,8 @@ namespace __format
> {
> auto __end = _M_do_parse(__pc, _AsBool);
> if (_M_spec._M_type == _Pres_s)
>- if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill)
>+ if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt
>+ || _M_spec._M_zero_fill)
> __throw_format_error("format error: format-spec contains "
> "invalid formatting options for "
> "'bool'");
>@@ -1589,8 +1613,9 @@ namespace __format
> else if constexpr (__char<_Tp>)
> {
> auto __end = _M_do_parse(__pc, _AsChar);
>- if (_M_spec._M_type == _Pres_c || _M_spec._M_type == _Pres_esc)
>- if (_M_spec._M_sign || _M_spec._M_alt || _M_spec._M_zero_fill
>+ if (_M_spec._M_type == _Pres_c)
>+ if (_M_spec._M_sign != _Sign_default || _M_spec._M_alt
>+ || _M_spec._M_zero_fill
> /* XXX should be invalid? || _M_spec._M_localized */)
> __throw_format_error("format error: format-spec contains "
> "invalid formatting options for "
>@@ -1702,51 +1727,34 @@ namespace __format
> _M_spec);
> }
>
>- [[__gnu__::__always_inline__]]
>- static size_t
>- _S_character_width(_CharT __c)
>- {
>- // N.B. single byte cannot encode charcter of width greater than 1
s/charcter/character/
>- if constexpr (sizeof(_CharT) > 1u &&
>- __unicode::__literal_encoding_is_unicode<_CharT>())
>- return __unicode::__field_width(__c);
>- else
>- return 1u;
>- }
>-
> template<typename _Out>
> typename basic_format_context<_Out, _CharT>::iterator
> _M_format_character(_CharT __c,
>- basic_format_context<_Out, _CharT>& __fc) const
>+ basic_format_context<_Out, _CharT>& __fc) const
> {
>- return __format::__write_padded_as_spec({&__c, 1u},
>- _S_character_width(__c),
>- __fc, _M_spec);
>- }
>+ basic_string_view<_CharT> __in(&__c, 1u);
>+ size_t __width = 1u;
>+ // N.B. single byte cannot encode charcter of width greater than 1
s/charcter/character/
>+ if constexpr (sizeof(_CharT) > 1u &&
>+ __unicode::__literal_encoding_is_unicode<_CharT>())
>+ __width = __unicode::__field_width(__c);
>
>- 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);
>+ if (!_M_spec._M_debug)
>+ return __format::__write_padded_as_spec(__in, __width,
>+ __fc, _M_spec);
>+
>+ __width += 2;
>+ if (_M_spec._M_get_width(__fc) <= __width)
>+ return __format::__write_escaped(__fc.out(), __in, _Term_apos);
>
> _CharT __buf[12];
>- __format::_Fixedbuf_sink<_CharT> __sink(__buf);
>- __format::__write_escaped(__sink.out(), __in, __term);
>+ _Fixedbuf_sink<_CharT> __sink(__buf);
>+ __format::__write_escaped(__sink.out(), __in, _Term_apos);
>
>- 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,
>+ __in = __sink.view();
>+ if (__in[1] == _Escapes<_CharT>::_S_bslash()[0]) // escape sequence
>+ __width = __in.size();
>+ return __format::__write_padded_as_spec(__in, __width,
> __fc, _M_spec);
> }
>
>@@ -2423,17 +2431,18 @@ namespace __format
> constexpr
> __formatter_ptr(_Spec<_CharT> __spec) noexcept
> : _M_spec(__spec)
>- { }
>+ { _M_set_default(_Pres_p); }
>
> constexpr typename basic_format_parse_context<_CharT>::iterator
>- parse(basic_format_parse_context<_CharT>& __pc)
>+ parse(basic_format_parse_context<_CharT>& __pc, _Pres_type __type = _Pres_p)
> {
> __format::_Spec<_CharT> __spec{};
> const auto __last = __pc.end();
> auto __first = __pc.begin();
>
>- auto __finalize = [this, &__spec] {
>+ auto __finalize = [this, &__spec, __type] {
> _M_spec = __spec;
>+ _M_set_default(__type);
> };
>
> auto __finished = [&] {
>@@ -2461,19 +2470,23 @@ namespace __format
> #endif
>
> __first = __spec._M_parse_width(__first, __last, __pc);
>+ if (__finished())
>+ return __first;
>
>- if (__first != __last)
>+ if (*__first == 'p')
> {
>- if (*__first == 'p')
>- ++__first;
>+ __spec._M_type = _Pres_p;
>+ _M_spec._M_alt = !_M_spec._M_alt;
>+ ++__first;
>+ }
> #if __glibcxx_format >= 202304L
>- else if (*__first == 'P')
>- {
>- __spec._M_type = __format::_Pres_P;
>- ++__first;
>- }
>-#endif
>+ else if (*__first == 'P')
>+ {
>+ __spec._M_type = _Pres_P;
>+ _M_spec._M_alt = !_M_spec._M_alt;
>+ ++__first;
> }
>+#endif
>
> if (__finished())
> return __first;
>@@ -2540,6 +2553,17 @@ namespace __format
> }
>
> private:
>+ [[__gnu__::__always_inline__]]
>+ constexpr void
>+ _M_set_default(_Pres_type __type)
>+ {
>+ if (_M_spec._M_type == _Pres_none && __type != _Pres_none)
>+ {
>+ _M_spec._M_type = __type;
>+ _M_spec._M_alt = !_M_spec._M_alt;
>+ }
>+ }
>+
> __format::_Spec<_CharT> _M_spec{};
> };
>
>@@ -2562,11 +2586,8 @@ namespace __format
> typename basic_format_context<_Out, _CharT>::iterator
> format(_CharT __u, basic_format_context<_Out, _CharT>& __fc) const
> {
>- if (_M_f._M_spec._M_type == __format::_Pres_none
>- || _M_f._M_spec._M_type == __format::_Pres_c)
>+ if (_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)
>- return _M_f._M_format_character_escaped(__u, __fc);
> else
> return _M_f.format(static_cast<make_unsigned_t<_CharT>>(__u), __fc);
> }
>@@ -2574,7 +2595,7 @@ namespace __format
> #if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> constexpr void
> set_debug_format() noexcept
>- { _M_f._M_spec._M_type = __format::_Pres_esc; }
>+ { _M_f._M_spec._M_debug = true; }
> #endif
>
> private:
>@@ -2598,11 +2619,8 @@ namespace __format
> typename basic_format_context<_Out, wchar_t>::iterator
> format(char __u, basic_format_context<_Out, wchar_t>& __fc) const
> {
>- if (_M_f._M_spec._M_type == __format::_Pres_none
>- || _M_f._M_spec._M_type == __format::_Pres_c)
>+ if (_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)
>- return _M_f._M_format_character_escaped(__u, __fc);
> else
> return _M_f.format(static_cast<unsigned char>(__u), __fc);
> }
>@@ -2610,7 +2628,7 @@ namespace __format
> #if __glibcxx_format_ranges // C++ >= 23 && HOSTED
> constexpr void
> set_debug_format() noexcept
>- { _M_f._M_spec._M_type = __format::_Pres_esc; }
>+ { _M_f._M_spec._M_debug = true; }
> #endif
>
> private:
>@@ -3864,7 +3882,7 @@ namespace __format
> }
> };
>
>- enum _Arg_t : unsigned char {
>+ enum class _Arg_t : unsigned char {
> _Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull,
> _Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle,
> _Arg_i128, _Arg_u128, _Arg_float128,
>@@ -3876,6 +3894,7 @@ namespace __format
> _Arg_ieee128 = _Arg_float128,
> #endif
> };
>+ using enum _Arg_t;
>
> template<typename _Context>
> struct _Arg_value
>@@ -4488,7 +4507,7 @@ namespace __format
> {
> __UINT64_TYPE__ __packed_types = 0;
> for (auto __i = __types.rbegin(); __i != __types.rend(); ++__i)
>- __packed_types = (__packed_types << _Bits) | *__i;
>+ __packed_types = (__packed_types << _Bits) | (unsigned)*__i;
> return __packed_types;
> }
> } // namespace __format
>@@ -4501,7 +4520,7 @@ namespace __format
> static constexpr int _S_packed_type_mask = 0b11111;
> static constexpr int _S_max_packed_args = 12;
>
>- static_assert( __format::_Arg_max_ <= (1 << _S_packed_type_bits) );
>+ static_assert( (unsigned)__format::_Arg_max_ <= (1u << _S_packed_type_bits) );
>
> template<typename... _Args>
> using _Store = __format::_Arg_store<_Context, _Args...>;
>@@ -5849,7 +5868,7 @@ namespace __format
> if (*__first == '?')
> {
> ++__first;
>- __spec._M_type = __format::_Pres_esc;
>+ __spec._M_debug = true;
> if (__finished() || *__first != 's')
> __throw_format_error("format error: '?' is allowed only in"
> " combination with 's'");
>@@ -5860,8 +5879,7 @@ namespace __format
> ++__first;
> if constexpr (same_as<_Tp, _CharT>)
> {
>- if (__spec._M_type != __format::_Pres_esc)
>- __spec._M_type = __format::_Pres_str;
>+ __spec._M_type = __format::_Pres_s;
> if (__finished())
> return __finalize();
> __throw_format_error("format error: element format specifier"
>@@ -5943,8 +5961,7 @@ namespace __format
> _M_format(_Rg& __rg, basic_format_context<_Out, _CharT>& __fc) const
> {
> if constexpr (same_as<_Tp, _CharT>)
>- if (_M_spec._M_type == __format::_Pres_str
>- || _M_spec._M_type == __format::_Pres_esc)
>+ if (_M_spec._M_type == __format::_Pres_s)
> {
> __format::__formatter_str __fstr(_M_spec);
> return __fstr._M_format_range(__rg, __fc);
>--
>2.49.0
>
>
More information about the Libstdc++
mailing list