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
>
>

Reply via email to