[PATCH 4/4] libstdc++: Rework formatting of empty chrono-spec for duration.
Jonathan Wakely
jwakely@redhat.com
Thu Jun 12 13:34:53 GMT 2025
On 06/06/25 12:41 +0200, Tomasz Kamiński wrote:
>In contrast to other calendar types if empty chron-spec is used for duration
"chron" -> "chrono"
>we are required to format it (and it's representation type) via ostream.
"it's" -> "its"
>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.
>
>As we no longer __formatter_chrono::_M_format with empty chrono-spec,
"no longer" -> "no longer use"
>this functions now requires that _M_chrono_specs are not empty,
"functions" -> "function"
>and onditional call to _M_format_to_ostream is removed. This allows
"onditional" -> "conditional"
>_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.
This is OK for trunk, although I think you can't push it until the
[PATCH 3/4] is in, so that _M_format_to_ostream isn't needed for the
info types.
>---
> 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
>
>
More information about the Libstdc++
mailing list