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.
Instead of `_M_reserved` and `_M_reserved2` allocated bitfields, the members of _Spec<_CharT> are rearranged so the class contains 16bit of tail padding. 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. 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. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (_ChronoSpec<_CharT>::_M_locale_specific): Declare as bit filed in tail-padding.. * 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. (_Spec<_CharT>): Rearranged members to have 16bits of tail-padding. (_Spec<_CharT>::_M_debug): Defined. (_Spec<_CharT>::_M_reserved, _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. (__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_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>::parse): Add _type parameter and set _M_alt to true. (__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. --- This patch shows alternative direction of using tail-padding in _Spec. I reserved all 16bits for the specializaed formatters. libstdc++-v3/include/bits/chrono_io.h | 18 +-- libstdc++-v3/include/bits/formatfwd.h | 3 +- libstdc++-v3/include/std/format | 156 +++++++++++++------------- 3 files changed, 83 insertions(+), 94 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index b7f6f5f49e5..2b77eb2acc5 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; } @@ -514,7 +508,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&); @@ -816,7 +810,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 b3794b64b59..aa769132762 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 + /* 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,10 @@ 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 = ' '; + // This class has 16bit of tail padding, that can be used by specialized + // formatters, by using this class as base. using iterator = typename basic_string_view<_CharT>::iterator; @@ -562,7 +571,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 +580,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 +612,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 +880,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, @@ -1312,11 +1321,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 +1344,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 @@ -1388,7 +1400,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 +1453,7 @@ namespace __format constexpr void set_debug_format() noexcept - { _M_spec._M_type = _Pres_esc; } + { _M_spec._M_debug = true; } #endif private: @@ -1551,7 +1563,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 +1571,7 @@ namespace __format case '?': if (__type == _AsChar) { - __spec._M_type = _Pres_esc; + __spec._M_debug = true; ++__first; } #endif @@ -1580,7 +1592,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 +1602,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,36 +1716,23 @@ 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 - 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 + if constexpr (sizeof(_CharT) > 1u && + __unicode::__literal_encoding_is_unicode<_CharT>()) + __width = __unicode::__field_width(__c); + + if (!_M_spec._M_debug) + return __format::__write_padded_as_spec(__in, __width, + __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); @@ -1739,14 +1740,12 @@ namespace __format __format::_Fixedbuf_sink<_CharT> __sink(__buf); __format::__write_escaped(__sink.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(); + __in = __sink.view(); + if (__in[1] == _Escapes<_CharT>::_S_bslash()[0]) // escape sequence + __width = __in.size(); else - __estimated_width = 2 + _S_character_width(__c); - return __format::__write_padded_as_spec(__escaped, - __estimated_width, + __width += 2; + return __format::__write_padded_as_spec(__in, __width, __fc, _M_spec); } @@ -2422,9 +2421,11 @@ namespace __format { } 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{}; + __spec._M_type = __type; + __spec._M_alt = true; const auto __last = __pc.end(); auto __first = __pc.begin(); @@ -2558,11 +2559,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); } @@ -2570,7 +2568,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: @@ -2594,11 +2592,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); } @@ -2606,7 +2601,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: @@ -3792,7 +3787,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, @@ -3806,6 +3801,7 @@ namespace __format #endif _Arg_max_ }; + using enum _Arg_t; template<typename _Context> struct _Arg_value @@ -4373,7 +4369,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 @@ -4386,7 +4382,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...>; @@ -5725,7 +5721,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'"); @@ -5736,8 +5732,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" @@ -5819,8 +5814,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