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