On Fri, Jun 27, 2025 at 5:12 PM Tomasz Kamiński <tkami...@redhat.com> wrote:

> This patch lifts locale initialization from locale-specific handling
> methods
> into _M_format_to function, and pass the locale by const reference.
> To avoid uncessary computation of locale::classic(), we use
> _Optional_locale,
> and emplace __fc.locale() into it only for localized formatting
> (_M_spec._M_localized) or locale::classic() if chrono-spec contains locale
> specific specifiers (_M_spec._M_locale_specific).
> The later is inprecise, as locale::classic() is only needed for subset of
> locale-specific specifiers (%a, %A, %b, %B, %c, %p, %r) while
> _M_locale_specific
> is set for %x,%x and if O/E modifiers are used. However, default output
> are not
> impacted (they use %b), or in case when month (%b) and weekday (%a) is
> used, the
> locale::classic() is be constructed once for non-localized output.
>
> In _M_S we no longer guard quering of numpuct facet, with check that
> requires
> potentially equally expensive construction of locale::classic. We also mark
> localized as unlikely.
>
> The _M_locale method is no longer used in __formatter_chrono, and thus was
> moved to __formatter_duration.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/chrono_io.h (__formatter_chrono::_M_format_to):
>         Compute locale and pass it to specifiers method.
>         (__formatter_chrono::_M_a_A, __formatter_chrono::_M_b_B)
>         (__formatter_chrono::_M_c, __formatter_chrono::_M_p)
>         (__formatter_chrono::_M_r): Accept locale instead of format
> context.
>         (__formatter_chrono::_M_subsecs): Call __ctx.locale() directly,
>         instead of _M_locale and do not compare with locale::classic().
>         Add [[unlikely]] attributes.
>         (__formatter_chrono::_M_locale): Move to __formatter_duration.
>         (__formatter_duration::_M_locale): Moved from __formatter_chrono.
> ---
> Doing this in separate patch, as I wanted to fix overlow warning soon.
> I have realized that we have dedicated _Optional_locale that I can use
> to avoid calling locale::classic().
> Testing on x86_64-linux. OK for trunk when all test passes?
>
All tests on x86_64-linux passed.

>
>  libstdc++-v3/include/bits/chrono_io.h | 71 ++++++++++++++++-----------
>  1 file changed, 43 insertions(+), 28 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/chrono_io.h
> b/libstdc++-v3/include/bits/chrono_io.h
> index bcf9830fb9e..a25cb9ada01 100644
> --- a/libstdc++-v3/include/bits/chrono_io.h
> +++ b/libstdc++-v3/include/bits/chrono_io.h
> @@ -964,10 +964,16 @@ namespace __format
>             return std::move(__out);
>           };
>
> +         _Optional_locale __loc;
> +         if (_M_spec._M_localized)
> +           __loc = __fc.locale();
> +         else if (_M_spec._M_locale_specific)
> +           __loc = locale::classic();
> +
>           struct tm __tm{};
>           bool __use_locale_fmt = false;
>           if (_M_spec._M_localized && _M_spec._M_locale_specific)
> -           if (__fc.locale() != locale::classic())
> +           if (__loc.value() != locale::classic())
>               {
>                 __use_locale_fmt = true;
>
> @@ -1004,7 +1010,7 @@ namespace __format
>             {
>               _CharT __c = *__first++;
>               if (__use_locale_fmt && _S_localized_spec(__c, __mod))
> [[unlikely]]
> -               __out = _M_locale_fmt(std::move(__out), __fc.locale(),
> +               __out = _M_locale_fmt(std::move(__out), __loc.value(),
>                                       __tm, __c, __mod);
>               else switch (__c)
>                 {
> @@ -1014,15 +1020,17 @@ namespace __format
>                   break;
>                 case 'a':
>                 case 'A':
> -                 __out = _M_a_A(__t._M_weekday, std::move(__out), __fc,
> __c == 'A');
> +                 __out = _M_a_A(__t._M_weekday, std::move(__out),
> +                                __loc.value(), __c == 'A');
>                   break;
>                 case 'b':
>                 case 'h':
>                 case 'B':
> -                 __out = _M_b_B(__t._M_month, std::move(__out), __fc, __c
> == 'B');
> +                 __out = _M_b_B(__t._M_month, std::move(__out),
> +                                __loc.value(), __c == 'B');
>                   break;
>                 case 'c':
> -                 __out = _M_c(__t, std::move(__out), __fc);
> +                 __out = _M_c(__t, std::move(__out), __loc.value());
>                   break;
>                 case 'C':
>                 case 'y':
> @@ -1058,7 +1066,7 @@ namespace __format
>                   __out = _M_M(__t._M_minutes, __print_sign());
>                   break;
>                 case 'p':
> -                 __out = _M_p(__t._M_hours, std::move(__out), __fc);
> +                 __out = _M_p(__t._M_hours, std::move(__out),
> __loc.value());
>                   break;
>                 case 'q':
>                   __out = _M_q(__t._M_unit_suffix, std::move(__out));
> @@ -1067,7 +1075,7 @@ namespace __format
>                   __out = _M_Q(__t, __print_sign(), __fc);
>                   break;
>                 case 'r':
> -                 __out = _M_r(__t, __print_sign(), __fc);
> +                 __out = _M_r(__t, __print_sign(), __loc.value());
>                   break;
>                 case 'R':
>                 case 'X':
> @@ -1146,10 +1154,10 @@ namespace __format
>           return std::move(__out);
>         }
>
> -      template<typename _OutIter, typename _FormatContext>
> +      template<typename _OutIter>
>         _OutIter
>         _M_a_A(chrono::weekday __wd, _OutIter __out,
> -              _FormatContext& __ctx, bool __full) const
> +              const locale& __loc, bool __full) const
>         {
>           // %a Locale's abbreviated weekday name.
>           // %A Locale's full weekday name.
> @@ -1165,7 +1173,6 @@ namespace __format
>                         __string_view(_GLIBCXX_WIDEN(" is not a valid
> weekday")));
>             }
>
> -         locale __loc = _M_locale(__ctx);
>           const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
>           const _CharT* __days[7];
>           if (__full)
> @@ -1176,10 +1183,10 @@ namespace __format
>           return _M_write(std::move(__out), __loc, __str);
>         }
>
> -      template<typename _OutIter, typename _FormatContext>
> +      template<typename _OutIter>
>         _OutIter
>         _M_b_B(chrono::month __m, _OutIter __out,
> -              _FormatContext& __ctx, bool __full) const
> +              const locale& __loc, bool __full) const
>         {
>           // %b Locale's abbreviated month name.
>           // %B Locale's full month name.
> @@ -1195,7 +1202,6 @@ namespace __format
>                         __string_view(_GLIBCXX_WIDEN(" is not a valid
> month")));
>             }
>
> -         locale __loc = _M_locale(__ctx);
>           const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
>           const _CharT* __months[12];
>           if (__full)
> @@ -1206,17 +1212,17 @@ namespace __format
>           return _M_write(std::move(__out), __loc, __str);
>         }
>
> -      template<typename _OutIter, typename _FormatContext>
> +      template<typename _OutIter>
>         _OutIter
>         _M_c(const _ChronoData<_CharT>& __t, _OutIter __out,
> -            _FormatContext& __ctx) const
> +            const locale& __loc) const
>         {
>           // %c  Locale's date and time representation, for C-locale: %a
> %b %e %T %Y
>           // %Ec Locale's alternate date and time representation, for
> C-locale same as above
>
> -         __out = _M_a_A(__t._M_weekday, std::move(__out), __ctx, false);
> +         __out = _M_a_A(__t._M_weekday, std::move(__out), __loc, false);
>           *__out = _S_space;
> -         __out = _M_b_B(__t._M_month, std::move(++__out), __ctx, false);
> +         __out = _M_b_B(__t._M_month, std::move(++__out), __loc, false);
>           *__out = _S_space;
>           __out = _M_d_e(__t._M_day, std::move(++__out), 'e');
>           *__out = _S_space;
> @@ -1458,16 +1464,15 @@ namespace __format
>           return __format::__write(std::move(__out), _S_two_digits(__i));
>         }
>
> -      template<typename _OutIter, typename _FormatContext>
> +      template<typename _OutIter>
>         _OutIter
> -       _M_p(chrono::hours __h, _OutIter __out, _FormatContext& __ctx)
> const
> +       _M_p(chrono::hours __h, _OutIter __out, const locale& __loc) const
>         {
>           // %p The locale's equivalent of the AM/PM designations.
>           auto __hi =  __h.count();
>           if (__hi >= 24) [[unlikely]]
>             __hi %= 24;
>
> -         locale __loc = _M_locale(__ctx);
>           const auto& __tp = use_facet<__timepunct<_CharT>>(__loc);
>           const _CharT* __ampm[2];
>           __tp._M_am_pm(__ampm);
> @@ -1491,10 +1496,10 @@ namespace __format
>           return std::vformat_to(std::move(__out), _S_empty_spec,
> __t._M_ereps);
>         }
>
> -      template<typename _OutIter, typename _FormatContext>
> +      template<typename _OutIter>
>         _OutIter
>         _M_r(const _ChronoData<_CharT>& __t, _OutIter __out,
> -            _FormatContext& __ctx) const
> +            const locale& __loc) const
>         {
>           // %r Locale's 12-hour clock time, for C-locale: %I:%M:%S %p
>           auto __hi = __t._M_hours.count() % 12;
> @@ -1511,7 +1516,7 @@ namespace __format
>
>           __string_view __sv(__buf, 9);
>           __out = __format::__write(std::move(__out), __sv);
> -         return _M_p(__t._M_hours, std::move(__out), __ctx);
> +         return _M_p(__t._M_hours, std::move(__out), __loc);
>         }
>
>        template<typename _OutIter>
> @@ -1575,9 +1580,9 @@ namespace __format
>             return __out;
>
>           _CharT __dot = _S_dot;
> -         if (_M_spec._M_localized)
> -           if (auto __loc = __ctx.locale(); __loc != locale::classic())
> +         if (_M_spec._M_localized) [[unlikely]]
>             {
> +             auto __loc = __ctx.locale();
>               const auto& __np = use_facet<numpunct<_CharT>>(__loc);
>               __dot = __np.decimal_point();
>             }
> @@ -1587,8 +1592,8 @@ namespace __format
>           if (_M_spec._M_floating_point_rep)
>             {
>               _Str_sink<_CharT> __sink;
> -             if (_M_spec._M_localized && _M_spec._M_custom_rep)
> -                std::vformat_to(__sink.out(), _M_locale(__ctx),
> +             if (_M_spec._M_localized && _M_spec._M_custom_rep)
> [[unlikely]]
> +                std::vformat_to(__sink.out(), __ctx.locale(),
>                                  _GLIBCXX_WIDEN("{1:0.{2}Lf}"),
> __t._M_ereps);
>               else
>                  std::vformat_to(__sink.out(),
> @@ -1846,7 +1851,6 @@ namespace __format
>         };
>
>        using __formatter_chrono<_CharT>::__formatter_chrono;
> -      using __formatter_chrono<_CharT>::_M_locale;
>        using __formatter_chrono<_CharT>::_M_spec;
>
>        template<typename _Duration, typename _ParseContext>
> @@ -1876,6 +1880,17 @@ namespace __format
>           return __res;
>         }
>
> +      // Return the formatting locale.
> +      template<typename _FormatContext>
> +       std::locale
> +       _M_locale(_FormatContext& __fc) const
> +       {
> +         if (!_M_spec._M_localized)
> +           return std::locale::classic();
> +         else
> +           return __fc.locale();
> +       }
> +
>        // Format duration for empty chrono-specs, e.g. "{}" (C++20
> [time.format] p6).
>        template<typename _Rep, typename _Period, typename _FormatContext>
>         typename _FormatContext::iterator
> --
> 2.49.0
>
>

Reply via email to