From: Tomasz Kamiński Date: Fri, 6 Jun 2025 10:24:11 +0000 (+0200) Subject: libstdc++: Rework formatting of empty chrono-spec for duration. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a651e3f3a6b4eb66185a9066c8cefe5288cda575;p=thirdparty%2Fgcc.git libstdc++: Rework formatting of empty chrono-spec for duration. In contrast to other calendar types if empty chrono-spec is used for duration we are required to format it (and its representation type) via ostream. Handling this case was now moved to be part of the format function for duration. To facilitate that __formatter_chrono::_M_format_to_ostream function was made public. However, for standard integral types, we know the result of inserting them into ostream, and in consequence we can format them directly. This is handled by configuring default format spec to "%Q%q" for such types. As we no longer use __formatter_chrono::_M_format with empty chrono-spec, this function now requires that _M_chrono_specs are not empty, and conditional call to _M_format_to_ostream is removed. This allows _M_format_to_ostream to be reduced to accept only duration. libstdc++-v3/ChangeLog: * include/bits/chrono_io.h (__formatter_chrono::_M_format): Remove handling of empty _M_chrono_specs. (__formatter_chrono::_M_format_to_ostream): Changed to accept only chrono::duration and made public. (std::formatter, _CharT>): Configure __defSpec and handle empty chrono-spec locally. Reviewed-by: Jonathan Wakely Signed-off-by: Tomasz Kamiński --- diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 8b3b777559e..81ec4414f48 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -606,14 +606,12 @@ namespace __format // that we instantiate fewer different specializations. Similar to // _Sink_iter for std::format. Replace each _S_year, _S_day etc. with // member functions of that type. + // pre: !_M_spec._M_chrono_specs.empty() template typename _FormatContext::iterator _M_format(const _Tp& __t, _FormatContext& __fc, bool __is_neg = false) const { - if (_M_spec._M_chrono_specs.empty()) - return _M_format_to_ostream(__t, __fc, __is_neg); - #if defined _GLIBCXX_USE_NL_LANGINFO_L && __CHAR_BIT__ == 8 // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3565. Handling of encodings in localized formatting @@ -812,6 +810,24 @@ namespace __format return std::move(__out); } + // Format duration for empty chrono-specs, e.g. "{}" (C++20 [time.format] p6). + template + typename _FormatContext::iterator + _M_format_to_ostream(const chrono::duration<_Rep, _Period>& __d, + bool __is_neg, _FormatContext& __fc) const + { + basic_ostringstream<_CharT> __os; + __os.imbue(_M_locale(__fc)); + + if (__is_neg) [[unlikely]] + __os << _S_plus_minus[1]; + __os << __d; + + auto __str = std::move(__os).str(); + return __format::__write_padded_as_spec(__str, __str.size(), + __fc, _M_spec); + } + _ChronoSpec<_CharT> _M_spec; private: @@ -826,41 +842,6 @@ namespace __format return __fc.locale(); } - // Format for empty chrono-specs, e.g. "{}" (C++20 [time.format] p6). - // TODO: consider moving body of every operator<< into this function - // and use std::format("{}", t) to implement those operators. That - // would avoid std::format("{}", t) calling operator<< which calls - // std::format again. - template - typename _FormatContext::iterator - _M_format_to_ostream(const _Tp& __t, _FormatContext& __fc, - bool __is_neg) const - { - using ::std::chrono::__detail::__utc_leap_second; - using ::std::chrono::__detail::__local_time_fmt; - - basic_ostringstream<_CharT> __os; - __os.imbue(_M_locale(__fc)); - - if constexpr (__is_specialization_of<_Tp, __local_time_fmt>) - __builtin_trap(); - else if constexpr (__is_specialization_of<_Tp, __utc_leap_second>) - __builtin_trap(); - else if constexpr (chrono::__is_time_point_v<_Tp>) - __builtin_trap(); - else - { - if constexpr (chrono::__is_duration_v<_Tp>) - if (__is_neg) [[unlikely]] - __os << _S_plus_minus[1]; - __os << __t; - } - - auto __str = std::move(__os).str(); - return __format::__write_padded_as_spec(__str, __str.size(), - __fc, _M_spec); - } - static constexpr const _CharT* _S_chars = _GLIBCXX_WIDEN("0123456789:/ +-{}"); static constexpr _CharT _S_colon = _S_chars[10]; @@ -2029,7 +2010,7 @@ namespace __format parse(basic_format_parse_context<_CharT>& __pc) { using namespace __format; - auto __it = _M_f._M_parse(__pc, _Duration|_TimeOfDay); + auto __it = _M_f._M_parse(__pc, _Duration|_TimeOfDay, __defSpec); if constexpr (!is_floating_point_v<_Rep>) if (_M_f._M_spec._M_prec_kind != __format::_WP_none) __throw_format_error("format error: invalid precision for duration"); @@ -2051,16 +2032,37 @@ namespace __format using _URep = make_unsigned_t<_Rep>; auto __ucnt = -static_cast<_URep>(__d.count()); auto __ud = chrono::duration<_URep, _Period>(__ucnt); - return _M_f._M_format(__ud, __fc, true); + return _M_format(__ud, true, __fc); } else - return _M_f._M_format(-__d, __fc, true); + return _M_format(-__d, true, __fc); } - return _M_f._M_format(__d, __fc, false); + return _M_format(__d, false, __fc); } private: - __format::__formatter_chrono<_CharT> _M_f; + static constexpr __format::_ChronoSpec<_CharT> __defSpec = [] + { + __format::_ChronoSpec<_CharT> __res{}; + __res._M_localized = !is_integral_v<_Rep>; + if constexpr (is_integral_v<_Rep>) + __res._M_chrono_specs = _GLIBCXX_WIDEN("%Q%q"); + return __res; + }(); + + template + typename basic_format_context<_Out, _CharT>::iterator + _M_format(const chrono::duration<_Rep2, _Period>& __d, + bool __is_neg, + basic_format_context<_Out, _CharT>& __fc) const + { + if constexpr (!is_integral_v<_Rep>) + if (_M_f._M_spec._M_chrono_specs.empty()) + return _M_f._M_format_to_ostream(__d, __is_neg, __fc); + return _M_f._M_format(__d, __fc, __is_neg); + } + + __format::__formatter_chrono<_CharT> _M_f{__defSpec}; }; template<__format::__char _CharT>