From: Anlai Lu <[email protected]>

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]>
---
v4:
- remove default argument for _BufSize and pass it explicitly
- use 64 characters buffer for month/weekday, to make it consistient
  with month/weekday (and other types containing both) using 128 character
  buffer. 32 bit is sufficient to store UTF-8 localized names
- do not pass locale for local_info, at the output is not locale
  dependent
- put return and single braces in line for operator<< defintions

Tested again on x86-64_linux. Pushed tot trunk.


 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 dba96759b1b..c5170368f82 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>>
-- 
2.55.0

Reply via email to