[gcc r16-1496] libstdc++: Format empty chrono-spec for the sys_info and local_info directly.

Tomasz Kaminski tkaminsk@gcc.gnu.org
Fri Jun 13 06:36:37 GMT 2025


https://gcc.gnu.org/g:c550b5aff5227ff1298d43b060bbf3823559f383

commit r16-1496-gc550b5aff5227ff1298d43b060bbf3823559f383
Author: Tomasz Kamiński <tkaminsk@redhat.com>
Date:   Fri Jun 6 11:56:08 2025 +0200

    libstdc++: Format empty chrono-spec for the sys_info and local_info directly.
    
    This patch change implementation of the formatters for sys_info and local_info,
    so they no longer delegate to operator<< for ostream in case of empty spec.
    As this types may be only formatted with chrono-spec containing only %%, %t, %n
    specifiers and fill characters, we use a separate __formatter_chrono_info formatter.
    
    For empty chron-spec __formatter_chrono_info formats sys_info using format_to call
    with format specifier extracted from corresponding operator<<, that now delegates
    to format with empty spec. For local_info we replicate functionality of the operator<<.
    The alignment and padding is handled using an _Padding_sink.
    
    For non-empty spec, we delegate to __formatter_chrono::_M_format. As non-of the
    format specifiers depends on the formatted object, we pass chrono::day to avoid
    triggering additional specializations.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/chrono_io.h (__format::__formatter_chrono_info)
            [_GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI]: Define.
            (std::formatter<chrono::sys_info, _CharT>)
            (std::formatter<chrono::local_inf, _CharT>): Delegate to
            __format::__formatter_chrono_info.
            (std::operator<<(basic_ostream<_CharT, _Traits>& const sys_info&)):
            Use format on sys_info with empty format spec.
    
    Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
    Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>

Diff:
---
 libstdc++-v3/include/bits/chrono_io.h | 100 +++++++++++++++++++++++++++++-----
 1 file changed, 85 insertions(+), 15 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index 247d40c82d76..8b3b777559e3 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -1940,6 +1940,84 @@ namespace __format
 	}
     };
 
+#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
+  template<typename _CharT>
+    struct __formatter_chrono_info
+    {
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      parse(basic_format_parse_context<_CharT>& __pc)
+      { return _M_f._M_parse(__pc, _ChronoParts(), {}); }
+
+      template<typename _Info, typename _Out>
+	typename basic_format_context<_Out, _CharT>::iterator
+	format(const _Info& __i,
+	       basic_format_context<_Out, _CharT>& __fc) const
+	{
+	  // n.b. only acceptable chrono-spec for info is one containing
+	  // only whitespaces and %%, that do not depend on formatted object.
+	  if (!_M_f._M_spec._M_chrono_specs.empty()) [[unlikely]]
+	    return _M_f._M_format(chrono::day(1), __fc);
+
+	  const size_t __padwidth = _M_f._M_spec._M_get_width(__fc);
+	  if (__padwidth == 0)
+	    return _M_format_to(__fc.out(), __i);
+
+	  _Padding_sink<_Out, _CharT> __sink(__fc.out(), __padwidth);
+	  _M_format_to(__sink.out(), __i);
+	  return __sink._M_finish(_M_f._M_spec._M_align, _M_f._M_spec._M_fill);
+	}
+
+    private:
+       template<typename _Out>
+	 _Out
+	 _M_format_to(_Out __out, const chrono::sys_info& __si) const
+	 {
+	   using _FmtStr = _Runtime_format_string<_CharT>;
+	   // n.b. only decimal separator is locale dependent for specifiers
+	   // used below, as sys_info uses seconds and minutes duration, the
+	   // output is locale-independent.
+	   constexpr auto* __fs
+	     = _GLIBCXX_WIDEN("[{0:%F %T},{1:%F %T},{2:%T},{3:%Q%q},{0:%Z}]");
+	   const chrono::local_seconds __lb(__si.begin.time_since_epoch());
+	   return std::format_to(std::move(__out), _FmtStr(__fs),
+				 chrono::local_time_format(__lb, &__si.abbrev),
+				 __si.end, __si.offset, __si.save);
+	 }
+
+       template<typename _Out>
+	 _Out
+	 _M_format_to(_Out __out, const chrono::local_info& __li) const
+	 {
+	   *__out = _Separators<_CharT>::_S_squares()[0];
+	   ++__out;
+	   if (__li.result == chrono::local_info::unique)
+	     __out = _M_format_to(std::move(__out), __li.first);
+	   else
+	    {
+	      basic_string_view<_CharT> __sv;
+	      if (__li.result == chrono::local_info::nonexistent)
+		__sv =_GLIBCXX_WIDEN("nonexistent");
+	      else
+		__sv = _GLIBCXX_WIDEN("ambiguous");
+	      __out = __format::__write(std::move(__out), __sv);
+
+	      __sv = _GLIBCXX_WIDEN(" local time between ");
+	      __out = __format::__write(std::move(__out), __sv);
+	      __out = _M_format_to(std::move(__out), __li.first);
+
+	      __sv = _GLIBCXX_WIDEN(" and ");
+	      __out = __format::__write(std::move(__out), __sv);
+	      __out = _M_format_to(std::move(__out), __li.second);
+	    }
+	  *__out = _Separators<_CharT>::_S_squares()[1];
+	  ++__out;
+	  return std::move(__out);
+	}
+
+      __formatter_chrono<_CharT> _M_f;
+    };
+#endif
+
 } // namespace __format
 /// @endcond
 
@@ -2430,16 +2508,16 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_ChronoParts{}); }
+      { return _M_f.parse(__pc); }
 
       template<typename _Out>
 	typename basic_format_context<_Out, _CharT>::iterator
 	format(const chrono::sys_info& __i,
 	       basic_format_context<_Out, _CharT>& __fc) const
-	{ return _M_f._M_format(__i, __fc); }
+	{ return _M_f.format(__i, __fc); }
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
+      __format::__formatter_chrono_info<_CharT> _M_f;
     };
 
   template<__format::__char _CharT>
@@ -2447,16 +2525,16 @@ namespace __format
     {
       constexpr typename basic_format_parse_context<_CharT>::iterator
       parse(basic_format_parse_context<_CharT>& __pc)
-      { return _M_f._M_parse(__pc, __format::_ChronoParts{}); }
+      { return _M_f.parse(__pc); }
 
       template<typename _Out>
 	typename basic_format_context<_Out, _CharT>::iterator
 	format(const chrono::local_info& __i,
 	       basic_format_context<_Out, _CharT>& __fc) const
-	{ return _M_f._M_format(__i, __fc); }
+	{ return _M_f.format(__i, __fc); }
 
     private:
-      __format::__formatter_chrono<_CharT> _M_f;
+      __format::__formatter_chrono_info<_CharT> _M_f;
     };
 #endif
 
@@ -3318,15 +3396,7 @@ namespace __detail
     basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __i)
     {
-      // n.b. only decimal separator is locale dependent for specifiers
-      // used below, as sys_info uses seconds and minutes duration, the
-      // output is locale-independent.
-      constexpr auto* __fs
-	= _GLIBCXX_WIDEN("[{0:%F %T},{1:%F %T},{2:%T},{3:%Q%q},{0:%Z}]");
-      local_seconds __lb(__i.begin.time_since_epoch());
-      __os << std::format(__fs, local_time_format(__lb, &__i.abbrev),
-			  __i.end, __i.offset, __i.save);
-      return __os;
+      return __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{}"), __i);
     }
 
   /// Writes a local_info object to an ostream in an unspecified format.


More information about the Libstdc++-cvs mailing list