On Mon, Jul 7, 2025 at 5:06 PM Tomasz Kamiński <[email protected]> wrote:
> With changes r16-2063-g8ad5968a8dcb47 the _M_a_A, _M_b_B and _M_p functions
> are called only if the locale is equal to the locale::classic(), for which
> the behavior is know. This patch changes they implementation, so instead of
> reffering to __timepunct facet members, they use hardcoded list of English
> weekday, months names. Only one list is needed, as in case of
> locale::classic()
> abbreviated name corresponds to first tree letters of the full name.
>
> For _M_p, _M_r we use a new _M_fill_ampm helper, that fills provided buffer
> with "AM"/"PM" depending on the hours value.
>
> In _M_S we no longer guard querying of numpuct facet, with check that
> requires
> potentially equally expensive construction of locale::classic. We also mark
> localized path as unlikely.
>
> The _M_locale method is no longer used in __formatter_chrono, and thus was
> moved to __formatter_duration.
>
> PR libstdc++/110739
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/chrono_io.h (__formatter_chrono::_S_weekdays)
> (__formatter_chrono::_S_months, __formatter_chrono::_S_fill_ampm):
> Define.
> (__formatter_chrono::_M_format_to): Do not pass context parameter
> to functions listed below.
> (__formatter_chrono::_M_a_A, __formatter_chrono::_M_b_B): Implement
> using harcoded list of names, and remove format context parameter.
> (__formatter_chrono::_M_p, __formatter_chrono::_M_r): Implement
> using _S_fill_ampm.
> (__formatter_chrono::_M_c): Removed format context parameter.
> (__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.
> ---
> Changing the approach, as we _M_a_A, _M_b_B and _M_p functions are no
> longer called for locale other than classic().
>
> Testing on x86_64-linux. Test for std/time/format* already passed.
> OK for trunk, when all test passes.
>
x86_64-linux test passed locally.
>
> libstdc++-v3/include/bits/chrono_io.h | 154 +++++++++++++++-----------
> 1 file changed, 87 insertions(+), 67 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/chrono_io.h
> b/libstdc++-v3/include/bits/chrono_io.h
> index 75ee7e818b2..fe3c9126749 100644
> --- a/libstdc++-v3/include/bits/chrono_io.h
> +++ b/libstdc++-v3/include/bits/chrono_io.h
> @@ -881,16 +881,32 @@ namespace __format
> _S_empty_fs()
> { return _Runtime_format_string<_CharT>(_S_empty_spec); }
>
> - // 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();
> - }
> + static constexpr const _CharT* _S_weekdays[]
> + {
> + _GLIBCXX_WIDEN("Sunday"),
> + _GLIBCXX_WIDEN("Monday"),
> + _GLIBCXX_WIDEN("Tuesday"),
> + _GLIBCXX_WIDEN("Wednesday"),
> + _GLIBCXX_WIDEN("Thursday"),
> + _GLIBCXX_WIDEN("Friday"),
> + _GLIBCXX_WIDEN("Saturday"),
> + };
> +
> + static constexpr const _CharT* _S_months[]
> + {
> + _GLIBCXX_WIDEN("January"),
> + _GLIBCXX_WIDEN("February"),
> + _GLIBCXX_WIDEN("March"),
> + _GLIBCXX_WIDEN("April"),
> + _GLIBCXX_WIDEN("May"),
> + _GLIBCXX_WIDEN("June"),
> + _GLIBCXX_WIDEN("July"),
> + _GLIBCXX_WIDEN("August"),
> + _GLIBCXX_WIDEN("September"),
> + _GLIBCXX_WIDEN("October"),
> + _GLIBCXX_WIDEN("November"),
> + _GLIBCXX_WIDEN("December"),
> + };
>
> private:
> template<typename _OutIter>
> @@ -1051,15 +1067,15 @@ 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), __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), __c ==
> 'B');
> break;
> case 'c':
> - __out = _M_c(__t, std::move(__out), __fc);
> + __out = _M_c(__t, std::move(__out));
> break;
> case 'C':
> case 'y':
> @@ -1095,7 +1111,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));
> break;
> case 'q':
> __out = _M_q(__t._M_unit_suffix, std::move(__out));
> @@ -1104,7 +1120,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());
> break;
> case 'R':
> case 'X':
> @@ -1183,10 +1199,9 @@ 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
> + _M_a_A(chrono::weekday __wd, _OutIter __out, bool __full) const
> {
> // %a Locale's abbreviated weekday name.
> // %A Locale's full weekday name.
> @@ -1199,21 +1214,15 @@ 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)
> - __tp._M_days(__days);
> - else
> - __tp._M_days_abbreviated(__days);
> - __string_view __str(__days[__wd.c_encoding()]);
> - return _M_write(std::move(__out), __loc, __str);
> + __string_view __str = _S_weekdays[__wd.c_encoding()];
> + if (!__full)
> + __str = __str.substr(0, 3);
> + return __format::__write(std::move(__out), __str);
> }
>
> - template<typename _OutIter, typename _FormatContext>
> + template<typename _OutIter>
> _OutIter
> - _M_b_B(chrono::month __m, _OutIter __out,
> - _FormatContext& __ctx, bool __full) const
> + _M_b_B(chrono::month __m, _OutIter __out, bool __full) const
> {
> // %b Locale's abbreviated month name.
> // %B Locale's full month name.
> @@ -1226,28 +1235,22 @@ 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)
> - __tp._M_months(__months);
> - else
> - __tp._M_months_abbreviated(__months);
> - __string_view __str(__months[(unsigned)__m - 1]);
> - return _M_write(std::move(__out), __loc, __str);
> + __string_view __str = _S_months[(unsigned)__m - 1];
> + if (!__full)
> + __str = __str.substr(0, 3);
> + return __format::__write(std::move(__out), __str);
> }
>
> - template<typename _OutIter, typename _FormatContext>
> + template<typename _OutIter>
> _OutIter
> - _M_c(const _ChronoData<_CharT>& __t, _OutIter __out,
> - _FormatContext& __ctx) const
> + _M_c(const _ChronoData<_CharT>& __t, _OutIter __out) 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), 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), false);
> *__out = _S_space;
> __out = _M_d_e(__t._M_day, std::move(++__out), 'e');
> *__out = _S_space;
> @@ -1489,20 +1492,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
> {
> // %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);
> - return _M_write(std::move(__out), __loc, __ampm[__hi >= 12]);
> +
> + _CharT __buf[2];
> + _S_fill_ampm(__buf, __h);
> + return __format::__write(std::move(__out), __string_view(__buf,
> 2));
> }
>
> template<typename _OutIter>
> @@ -1522,27 +1520,25 @@ 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
> + _M_r(const _ChronoData<_CharT>& __t, _OutIter __out) const
> {
> // %r Locale's 12-hour clock time, for C-locale: %I:%M:%S %p
> auto __hi = __t._M_hours.count() % 12;
> if (__hi == 0)
> __hi = 12;
>
> - _CharT __buf[9];
> + _CharT __buf[11];
> __buf[2] = _S_colon;
> __buf[5] = _S_colon;
> __buf[8] = _S_space;
> _S_fill_two_digits(__buf, __hi);
> _S_fill_two_digits(__buf + 3, __t._M_minutes.count());
> _S_fill_two_digits(__buf + 6, __t._M_seconds.count());
> + _S_fill_ampm(__buf + 9, __t._M_hours);
>
> - __string_view __sv(__buf, 9);
> - __out = __format::__write(std::move(__out), __sv);
> - return _M_p(__t._M_hours, std::move(__out), __ctx);
> + return __format::__write(std::move(__out), __string_view(__buf,
> 11));
> }
>
> template<typename _OutIter>
> @@ -1606,9 +1602,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();
> }
> @@ -1618,8 +1614,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(),
> @@ -1778,6 +1774,20 @@ namespace __format
> __buf[1] = __sv[1];
> }
>
> + // Fills __buf[0] and __buf[1] with "AM", "PM" depending on __h.
> + [[__gnu__::__always_inline__]]
> + static void
> + _S_fill_ampm(_CharT* __buf, chrono::hours __h)
> + {
> + auto __hi = __h.count();
> + if (__hi >= 24) [[unlikely]]
> + __hi %= 24;
> +
> + constexpr const _CharT* __apm = _GLIBCXX_WIDEN("APM");
> + __buf[0] = __apm[__hi >= 12];
> + __buf[1] = __apm[2];
> + }
> +
> // Returns decimal representation of __n.
> // Returned string_view may point to __buf.
> [[__gnu__::__always_inline__]]
> @@ -1879,7 +1889,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>
> @@ -1909,6 +1918,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
>
>