[gcc r17-2662] libstdc++: Use __chrono_write via _S_empty_fs for chrono ostream insertion

Tomasz Kaminski via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:e906bebbf21dc50a5c4db9afa358db6e48d4706b

commit r17-2662-ge906bebbf21dc50a5c4db9afa358db6e48d4706b
Author: Anlai Lu <[email protected]>
Date:   Tue Jun 30 12:49:03 2026 +0000

    libstdc++: Use __chrono_write via _S_empty_fs for chrono ostream insertion
    
    Add __detail::__chrono_write which formats a chrono object into a
    stack buffer via std::format_to_n with _S_empty_fs(), then writes
    through __ostream_insert. A non-type template parameter _BufSize
    allows per-type buffer tuning.  Each _BufSize is the smallest power
    of two that accommodates the longest possible formatted output for
    that type, including error cases.  All chrono operator<< overloads
    that previously used std::format, std::vformat, or basic_stringstream
    now use this helper.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/chrono_io.h (__formatter_chrono::_S_empty_fs):
            Make public.
            (__detail::__chrono_write): New function template.
            (operator<<): Use __chrono_write consistently for all chrono
            types.
    
    Reviewed-by: Jonathan Wakely <[email protected]>
    Reviewed-by: Tomasz Kamiński <[email protected]>
    Signed-off-by: Anlai Lu <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/chrono_io.h | 300 +++++++---------------------------
 1 file changed, 55 insertions(+), 245 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h
index dba96759b1b5..c5170368f82f 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -884,11 +884,13 @@ namespace __format
       static constexpr const _CharT* _S_minus_empty_spec = _S_chars + 17;
       static constexpr const _CharT* _S_empty_spec = _S_chars + 18;
 
+    public:
       [[__gnu__::__always_inline__]]
       static _Dynamic_format_string<_CharT>
       _S_empty_fs()
       { return _Dynamic_format_string<_CharT>(_S_empty_spec); }
 
+    protected:
       static constexpr const _CharT* _S_weekdays[]
       {
 	_GLIBCXX_WIDEN("Sunday"),
@@ -3607,6 +3609,32 @@ namespace __detail
 	}
     }
 
+  // Format into a stack buffer via std::format_to_n with the empty
+  // chrono-spec (_S_empty_fs), then write through __ostream_insert.
+  // The empty spec lets each formatter use its __defSpec, producing
+  // output equivalent to the corresponding operator<<.
+  // _BufSize allows per-type tuning of the stack buffer size.
+  template<size_t _BufSize, typename _CharT, typename _Traits,
+	   typename _Arg, typename... _OptLocale>
+    inline basic_ostream<_CharT, _Traits>&
+    __chrono_write(basic_ostream<_CharT, _Traits>& __os,
+		   const _Arg& __arg, const _OptLocale&... __loc)
+    {
+      static_assert(sizeof...(_OptLocale) <= 1);
+      constexpr auto __fs
+	= __format::__formatter_chrono<_CharT>::_S_empty_fs;
+      constexpr size_t __bufsize = _BufSize;
+      _CharT __buf[__bufsize];
+      auto __res = std::format_to_n(__buf, __bufsize, __loc...,
+				    __fs(), __arg);
+
+      if (static_cast<size_t>(__res.size) <= __bufsize) [[likely]]
+	return std::__ostream_insert(__os, __buf, __res.size);
+
+      auto __s = std::format(__loc..., __fs(), __arg);
+      return std::__ostream_insert(__os, __s.data(), __s.size());
+    }
+
 } // namespace __detail
 /// @endcond
 
@@ -3628,16 +3656,7 @@ namespace __detail
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const day& __d)
-    {
-      using _Ctx = __format::__format_context<_CharT>;
-      using _Str = basic_string_view<_CharT>;
-      _Str __s = _GLIBCXX_WIDEN("{:02d} is not a valid day");
-      if (__d.ok())
-	__s = __s.substr(0, 6);
-      auto __u = (unsigned)__d;
-      __os << std::vformat(__s, make_format_args<_Ctx>(__u));
-      return __os;
-    }
+    { return __detail::__chrono_write<32>(__os, __d); }
 
   template<typename _CharT, typename _Traits,
 	   typename _Alloc = allocator<_CharT>>
@@ -3656,20 +3675,7 @@ namespace __detail
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const month& __m)
-    {
-      using _Ctx = __format::__format_context<_CharT>;
-      using _Str = basic_string_view<_CharT>;
-      _Str __s = _GLIBCXX_WIDEN("{:L%b}{} is not a valid month");
-      if (__m.ok())
-	__os << std::vformat(__os.getloc(), __s.substr(0, 6),
-			     make_format_args<_Ctx>(__m));
-      else
-	{
-	  auto __u = (unsigned)__m;
-	  __os << std::vformat(__s.substr(6), make_format_args<_Ctx>(__u));
-	}
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __m, __os.getloc()); }
 
   template<typename _CharT, typename _Traits,
 	   typename _Alloc = allocator<_CharT>>
@@ -3688,20 +3694,7 @@ namespace __detail
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const year& __y)
-    {
-      using _Ctx = __format::__format_context<_CharT>;
-      using _Str = basic_string_view<_CharT>;
-      _Str __s = _GLIBCXX_WIDEN("-{:04d} is not a valid year");
-      if (__y.ok())
-	__s = __s.substr(0, 7);
-      int __i = (int)__y;
-      if (__i >= 0) [[likely]]
-	__s.remove_prefix(1);
-      else
-	__i = -__i;
-      __os << std::vformat(__s, make_format_args<_Ctx>(__i));
-      return __os;
-    }
+    { return __detail::__chrono_write<32>(__os, __y); }
 
   template<typename _CharT, typename _Traits,
 	   typename _Alloc = allocator<_CharT>>
@@ -3720,20 +3713,7 @@ namespace __detail
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const weekday& __wd)
-    {
-      using _Ctx = __format::__format_context<_CharT>;
-      using _Str = basic_string_view<_CharT>;
-      _Str __s = _GLIBCXX_WIDEN("{:L%a}{} is not a valid weekday");
-      if (__wd.ok())
-	__os << std::vformat(__os.getloc(), __s.substr(0, 6),
-			     make_format_args<_Ctx>(__wd));
-      else
-	{
-	  auto __c = __wd.c_encoding();
-	  __os << std::vformat(__s.substr(6), make_format_args<_Ctx>(__c));
-	}
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __wd, __os.getloc()); }
 
   template<typename _CharT, typename _Traits,
 	   typename _Alloc = allocator<_CharT>>
@@ -3753,55 +3733,18 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const weekday_indexed& __wdi)
-    {
-      // The standard says to format wdi.weekday() and wdi.index() using
-      // either "{:L}[{}]" or "{:L}[{} is not a valid index]". The {:L} spec
-      // means to format the weekday using ostringstream, so just do that.
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __wdi.weekday();
-      const auto __i = __wdi.index();
-      basic_string_view<_CharT> __s
-	= _GLIBCXX_WIDEN("[ is not a valid index]");
-      __os2 << __s[0];
-      __os2 << std::format(_GLIBCXX_WIDEN("{}"), __i);
-      if (__i >= 1 && __i <= 5)
-	__os2 << __s.back();
-      else
-	__os2 << __s.substr(1);
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __wdi, __os.getloc()); }
 
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const weekday_last& __wdl)
-    {
-      // As above, just write straight to a stringstream, as if by "{:L}[last]"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __wdl.weekday() << _GLIBCXX_WIDEN("[last]");
-      __os << __os2.view();
-      return __os;
-    }
-
+    { return __detail::__chrono_write<128>(__os, __wdl, __os.getloc()); }
+    
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const month_day& __md)
-    {
-      // As above, just write straight to a stringstream, as if by "{:L}/{}"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __md.month();
-      if constexpr (is_same_v<_CharT, char>)
-	__os2 << '/';
-      else
-	__os2 << L'/';
-      __os2 << __md.day();
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __md, __os.getloc()); }
 
   template<typename _CharT, typename _Traits,
 	   typename _Alloc = allocator<_CharT>>
@@ -3823,67 +3766,24 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const month_day_last& __mdl)
-    {
-      // As above, just write straight to a stringstream, as if by "{:L}/last"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __mdl.month() << _GLIBCXX_WIDEN("/last");
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __mdl, __os.getloc()); }
 
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const month_weekday& __mwd)
-    {
-      // As above, just write straight to a stringstream, as if by "{:L}/{:L}"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __mwd.month();
-      if constexpr (is_same_v<_CharT, char>)
-	__os2 << '/';
-      else
-	__os2 << L'/';
-      __os2 << __mwd.weekday_indexed();
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __mwd, __os.getloc()); }
 
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const month_weekday_last& __mwdl)
-    {
-      // As above, just write straight to a stringstream, as if by "{:L}/{:L}"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __mwdl.month();
-      if constexpr (is_same_v<_CharT, char>)
-	__os2 << '/';
-      else
-	__os2 << L'/';
-      __os2 << __mwdl.weekday_last();
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __mwdl, __os.getloc()); }
 
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month& __ym)
-    {
-      // As above, just write straight to a stringstream, as if by "{}/{:L}"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __ym.year();
-      if constexpr (is_same_v<_CharT, char>)
-	__os2 << '/';
-      else
-	__os2 << L'/';
-      __os2 << __ym.month();
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __ym, __os.getloc()); }
 
   template<typename _CharT, typename _Traits,
 	   typename _Alloc = allocator<_CharT>>
@@ -3905,14 +3805,7 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const year_month_day& __ymd)
-    {
-      using _Ctx = __format::__format_context<_CharT>;
-      using _Str = basic_string_view<_CharT>;
-      _Str __s = _GLIBCXX_WIDEN("{:%F} is not a valid date");
-      __os << std::vformat(__ymd.ok() ? __s.substr(0, 5) : __s,
-			   make_format_args<_Ctx>(__ymd));
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __ymd); }
 
   template<typename _CharT, typename _Traits,
 	   typename _Alloc = allocator<_CharT>>
@@ -3935,107 +3828,45 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const year_month_day_last& __ymdl)
-    {
-      // As above, just write straight to a stringstream, as if by "{}/{:L}"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      __os2 << __ymdl.year();
-      if constexpr (is_same_v<_CharT, char>)
-	__os2 << '/';
-      else
-	__os2 << L'/';
-      __os2 << __ymdl.month_day_last();
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __ymdl, __os.getloc()); }
 
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const year_month_weekday& __ymwd)
-    {
-      // As above, just write straight to a stringstream, as if by
-      // "{}/{:L}/{:L}"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      _CharT __slash;
-      if constexpr (is_same_v<_CharT, char>)
-	__slash = '/';
-      else
-	__slash = L'/';
-      __os2 << __ymwd.year() << __slash << __ymwd.month() << __slash
-	    << __ymwd.weekday_indexed();
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __ymwd, __os.getloc()); }
 
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const year_month_weekday_last& __ymwdl)
-    {
-      // As above, just write straight to a stringstream, as if by
-      // "{}/{:L}/{:L}"
-      basic_stringstream<_CharT> __os2;
-      __os2.imbue(__os.getloc());
-      _CharT __slash;
-      if constexpr (is_same_v<_CharT, char>)
-	__slash = '/';
-      else
-	__slash = L'/';
-      __os2 << __ymwdl.year() << __slash << __ymwdl.month() << __slash
-	    << __ymwdl.weekday_last();
-      __os << __os2.view();
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __ymwdl, __os.getloc()); }
 
   template<typename _CharT, typename _Traits, typename _Duration>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const hh_mm_ss<_Duration>& __hms)
-    {
-      return __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%T}"), __hms);
-    }
+    { return __detail::__chrono_write<64>(__os, __hms, __os.getloc()); }
 
 #if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI
   /// Writes a sys_info object to an ostream in an unspecified format.
   template<typename _CharT, typename _Traits>
     basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_info& __i)
-    {
-      return __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{}"), __i);
-    }
+    { return __detail::__chrono_write<128>(__os, __i); }
 
   /// Writes a local_info object to an ostream in an unspecified format.
   template<typename _CharT, typename _Traits>
     basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __li)
-    {
-      __os << __format::_Separators<_CharT>::_S_squares()[0];
-      if (__li.result == local_info::unique)
-	__os << __li.first;
-      else
-	{
-	  if (__li.result == local_info::nonexistent)
-	    __os << _GLIBCXX_WIDEN("nonexistent");
-	  else
-	    __os << _GLIBCXX_WIDEN("ambiguous");
-	  __os << _GLIBCXX_WIDEN(" local time between ") << __li.first;
-	  __os << _GLIBCXX_WIDEN(" and ") << __li.second;
-	}
-      __os << __format::_Separators<_CharT>::_S_squares()[1];
-      return __os;
-    }
+    { return __detail::__chrono_write<256>(__os, __li); }
 
   template<typename _CharT, typename _Traits, typename _Duration,
 	   typename _TimeZonePtr>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const zoned_time<_Duration, _TimeZonePtr>& __t)
-    {
-      __os << format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T %Z}"), __t);
-      return __os;
-    }
+    { return __detail::__chrono_write<128>(__os, __t, __os.getloc()); }
 #endif
 
   template<typename _CharT, typename _Traits, typename _Duration>
@@ -4044,18 +3875,12 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const sys_time<_Duration>& __tp)
-    {
-      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __tp);
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __tp, __os.getloc()); }
 
   template<typename _CharT, typename _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days& __dp)
-    {
-      __os << year_month_day{__dp};
-      return __os;
-    }
+    { return __detail::__chrono_write<32>(__os, __dp); };
 
   template<typename _CharT, typename _Traits, typename _Duration,
 	   typename _Alloc = allocator<_CharT>>
@@ -4089,10 +3914,7 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const utc_time<_Duration>& __t)
-    {
-      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t);
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); }
 
   template<typename _CharT, typename _Traits, typename _Duration,
 	   typename _Alloc = allocator<_CharT>>
@@ -4124,10 +3946,7 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const tai_time<_Duration>& __t)
-    {
-      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t);
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); }
 
   template<typename _CharT, typename _Traits, typename _Duration,
 	   typename _Alloc = allocator<_CharT>>
@@ -4163,10 +3982,7 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const gps_time<_Duration>& __t)
-    {
-      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t);
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); }
 
   template<typename _CharT, typename _Traits, typename _Duration,
 	   typename _Alloc = allocator<_CharT>>
@@ -4201,10 +4017,7 @@ namespace __detail
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __os,
 	       const file_time<_Duration>& __t)
-    {
-      __os << std::format(__os.getloc(), _GLIBCXX_WIDEN("{:L%F %T}"), __t);
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __t, __os.getloc()); }
 
   template<typename _CharT, typename _Traits, typename _Duration,
 	   typename _Alloc = allocator<_CharT>>
@@ -4227,10 +4040,7 @@ namespace __detail
     // _GLIBCXX_RESOLVE_LIB_DEFECTS
     // 4257. Stream insertion for chrono::local_time should be constrained
     requires requires(const sys_time<_Duration>& __st) { __os << __st; }
-    {
-      __os << sys_time<_Duration>{__lt.time_since_epoch()};
-      return __os;
-    }
+    { return __detail::__chrono_write<64>(__os, __lt, __os.getloc()); }
 
   template<typename _CharT, typename _Traits, typename _Duration,
 	   typename _Alloc = allocator<_CharT>>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.