On Thu, 12 Jun 2025, 16:24 Tomasz Kamiński, <tkami...@redhat.com> wrote:
> 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 that the result of inserting > them into ostream is same as for calling format. 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<chrono::duration<_Rep, _Period>, _CharT>): > Configure __defSpec and handle empty chrono-spec locally. > --- > The v2 patch uses "Qq" only for integral types, as there is difference > between osteam ouput for floating point and integral types. Adjusted > the description. > > Testing on x86_64-linux. OK for trunk? OK thanks > > libstdc++-v3/include/bits/chrono_io.h | 88 ++++++++++++++------------- > 1 file changed, 45 insertions(+), 43 deletions(-) > > diff --git a/libstdc++-v3/include/bits/chrono_io.h > b/libstdc++-v3/include/bits/chrono_io.h > index 5fa07364f64..40e80589059 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 _Tp, typename _FormatContext> > 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 _Rep, typename _Period, typename _FormatContext> > + 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 _Tp, typename _FormatContext> > - 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]; > @@ -2027,7 +2008,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"); > @@ -2049,16 +2030,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 _Rep2, typename _Out> > + 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> > -- > 2.49.0 > >