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 empty chrono-spec (with padding), they 
now
use a separate __formatter_chrono_info formatter.

The __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.

libstdc++-v3/ChangeLog:

        * include/bits/chrono_io.h (__format::__formatter_chrono_info):
        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.
---
 libstdc++-v3/include/bits/chrono_io.h | 132 +++++++++++++++++++++++---
 1 file changed, 117 insertions(+), 15 deletions(-)

diff --git a/libstdc++-v3/include/bits/chrono_io.h 
b/libstdc++-v3/include/bits/chrono_io.h
index 24c7dfb91c9..bbbae3d3064 100644
--- a/libstdc++-v3/include/bits/chrono_io.h
+++ b/libstdc++-v3/include/bits/chrono_io.h
@@ -1948,6 +1948,104 @@ namespace __format
        }
     };
 
+  template<typename _CharT>
+    struct __formatter_chrono_info
+    {
+      constexpr typename basic_format_parse_context<_CharT>::iterator
+      _M_parse(basic_format_parse_context<_CharT>& __pc)
+      {
+       auto __first = __pc.begin();
+       auto __last = __pc.end();
+
+       _Spec<_CharT> __spec{};
+
+       auto __finished = [&] {
+         if (__first == __last || *__first == '}')
+           {
+             _M_spec = __spec;
+             return true;
+           }
+         return false;
+       };
+
+       if (__finished())
+         return __first;
+
+       __first = __spec._M_parse_fill_and_align(__first, __last);
+       if (__finished())
+         return __first;
+
+       __first = __spec._M_parse_width(__first, __last, __pc);
+       if (__finished())
+         return __first;
+
+       return __spec._M_parse_locale(__first, __last);
+      }
+
+      template<typename _Info, typename _Out>
+       typename basic_format_context<_Out, _CharT>::iterator
+       format(const _Info& __i,
+              basic_format_context<_Out, _CharT>& __fc) const
+       {
+         const size_t __padwidth = _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_spec._M_align, _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);
+       }
+
+      _Spec<_CharT> _M_spec;
+    };
+
 } // namespace __format
 /// @endcond
 
@@ -2438,16 +2536,22 @@ 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{}); }
+      {
+       auto __res = _M_f._M_parse(__pc);
+       if (__res == __pc.end() || *__res == '}')
+         return __res;
+       std::__throw_format_error("format error: invalid format-spec for "
+                                  "std::chrono::sys_info");
+      }
 
       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>
@@ -2455,16 +2559,22 @@ 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{}); }
+      {
+       auto __res = _M_f._M_parse(__pc);
+       if (__res == __pc.end() || *__res == '}')
+         return __res;
+       std::__throw_format_error("format error: invalid format-spec for "
+                                  "std::chrono::local_info");
+      }
 
       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
 
@@ -3326,15 +3436,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.
-- 
2.49.0

Reply via email to