[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