On Fri, Jun 6, 2025 at 12:55 PM Tomasz Kamiński <tkami...@redhat.com> wrote:
> In contrast to other calendar types if empty chron-spec is used for > duration > we are required to format it (and it's 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 aritmetic 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. > There is a difference for floating point types, so we need to change that to integral, and two is_arithmetic_v with is_integral_v. > > As we no longer __formatter_chrono::_M_format with empty chrono-spec, > this functions now requires that _M_chrono_specs are not empty, > and onditional 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. > --- > 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 bbbae3d3064..d8744339094 100644 > --- a/libstdc++-v3/include/bits/chrono_io.h > +++ b/libstdc++-v3/include/bits/chrono_io.h > @@ -614,14 +614,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 > @@ -820,6 +818,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: > @@ -834,41 +850,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]; > @@ -2057,7 +2038,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"); > @@ -2079,16 +2060,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_arithmetic_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_arithmetic_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 > >