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