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): 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. --- Sending actual v2, with support for chrono-spec with whitespaces and non-value spec. Sorry for noise. libstdc++-v3/include/bits/chrono_io.h | 98 +++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/libstdc++-v3/include/bits/chrono_io.h b/libstdc++-v3/include/bits/chrono_io.h index 247d40c82d7..5fa07364f64 100644 --- a/libstdc++-v3/include/bits/chrono_io.h +++ b/libstdc++-v3/include/bits/chrono_io.h @@ -1940,6 +1940,82 @@ namespace __format } }; + 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; + }; + } // namespace __format /// @endcond @@ -2430,16 +2506,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 +2523,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 +3394,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