commit fcc13d6fc31441b5672b68a5e3b247687724218f Author: Jonathan Wakely Date: Thu Oct 7 19:58:07 2021 libstdc++: Implement ostream insertion for chrono::duration This is a missing piece of the C++20 header. It would be good to move the code into the compiled library, so that we don't need in . It could also use spanstream in C++20, to avoid memory allocations. That can be changed at a later date. libstdc++-v3/ChangeLog: * include/std/chrono (__detail::__units_suffix_misc): New helper function. (__detail::__units_suffix): Likewise. (chrono::operator<<(basic_ostream&, const duration&)): Define. * testsuite/20_util/duration/io.cc: New test. diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono index c8060d7a67e..0662e26348f 100644 --- a/libstdc++-v3/include/std/chrono +++ b/libstdc++-v3/include/std/chrono @@ -37,6 +37,10 @@ #else #include +#if __cplusplus > 201703L +# include // ostringstream +# include +#endif namespace std _GLIBCXX_VISIBILITY(default) { @@ -2077,6 +2081,101 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @} } // inline namespace chrono_literals } // inline namespace literals + + namespace chrono + { + /// @addtogroup chrono + /// @{ + + /// @cond undocumented + namespace __detail + { + template + const char* + __units_suffix_misc(char* __buf, size_t __n) noexcept + { + namespace __tc = std::__detail; + char* __p = __buf; + __p[0] = '['; + unsigned __nlen = __tc::__to_chars_len((uintmax_t)_Period::num); + __tc::__to_chars_10_impl(__p + 1, __nlen, (uintmax_t)_Period::num); + __p += 1 + __nlen; + if constexpr (_Period::den != 1) + { + __p[0] = '/'; + unsigned __dlen = __tc::__to_chars_len((uintmax_t)_Period::den); + __tc::__to_chars_10_impl(__p + 1, __dlen, (uintmax_t)_Period::den); + __p += 1 + __dlen; + } + __p[0] = ']'; + __p[1] = 's'; + __p[2] = '\0'; + return __buf; + } + + template + auto + __units_suffix(char* __buf, size_t __n) noexcept + { +#define _GLIBCXX_UNITS_SUFFIX(period, suffix) \ + if constexpr (is_same_v<_Period, period>) \ + { \ + if constexpr (is_same_v<_CharT, wchar_t>) \ + return L##suffix; \ + else \ + return suffix; \ + } \ + else + + _GLIBCXX_UNITS_SUFFIX(atto, "as") + _GLIBCXX_UNITS_SUFFIX(femto, "fs") + _GLIBCXX_UNITS_SUFFIX(pico, "ps") + _GLIBCXX_UNITS_SUFFIX(nano, "ns") + _GLIBCXX_UNITS_SUFFIX(micro, "\u00b5s") + _GLIBCXX_UNITS_SUFFIX(milli, "ms") + _GLIBCXX_UNITS_SUFFIX(centi, "cs") + _GLIBCXX_UNITS_SUFFIX(deci, "ds") + _GLIBCXX_UNITS_SUFFIX(ratio<1>, "s") + _GLIBCXX_UNITS_SUFFIX(deca, "das") + _GLIBCXX_UNITS_SUFFIX(hecto, "hs") + _GLIBCXX_UNITS_SUFFIX(kilo, "ks") + _GLIBCXX_UNITS_SUFFIX(mega, "Ms") + _GLIBCXX_UNITS_SUFFIX(giga, "Gs") + _GLIBCXX_UNITS_SUFFIX(tera, "Ts") + _GLIBCXX_UNITS_SUFFIX(tera, "Ts") + _GLIBCXX_UNITS_SUFFIX(peta, "Ps") + _GLIBCXX_UNITS_SUFFIX(exa, "Es") + _GLIBCXX_UNITS_SUFFIX(ratio<60>, "min") + _GLIBCXX_UNITS_SUFFIX(ratio<3600>, "h") + _GLIBCXX_UNITS_SUFFIX(ratio<86400>, "d") +#undef _GLIBCXX_UNITS_SUFFIX + return __detail::__units_suffix_misc<_Period>(__buf, __n); + } + } // namespace __detail + /// @endcond + + template + inline basic_ostream<_CharT, _Traits>& + operator<<(std::basic_ostream<_CharT, _Traits>& __os, + const duration<_Rep, _Period>& __d) + { + using period = typename _Period::type; + char __buf[sizeof("[/]s") + 2 * numeric_limits::digits10]; + std::basic_ostringstream<_CharT, _Traits> __s; + __s.flags(__os.flags()); + __s.imbue(__os.getloc()); + __s.precision(__os.precision()); + __s << __d.count(); + __s << __detail::__units_suffix(__buf, sizeof(__buf)); + __os << std::move(__s).str(); + return __os; + } + + // TODO: from_stream for duration + + /// @} group chrono + } // namespace chrono #endif // C++20 _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc new file mode 100644 index 00000000000..405e1afa440 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/duration/io.cc @@ -0,0 +1,54 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include +#include +#include + +void +test01() +{ + using namespace std::chrono; + std::stringstream ss; + ss << 0s << '\n'; + ss << 3h + 5min << '\n'; + ss << duration>(3) << '\n'; + ss << duration>(9) << '\n'; + std::string s; + std::getline(ss, s); + VERIFY( s == "0s" ); + std::getline(ss, s); + VERIFY( s == "185min" ); + std::getline(ss, s); + VERIFY( s == "3[2]s" ); + std::getline(ss, s); + VERIFY( s == "9[2/3]s" ); +} + +void +test02() +{ +#ifdef _GLIBCXX_USE_WCHAR_T + using namespace std::chrono; + std::wstringstream ss; + ss << 0s << L'\n'; + ss << 3h + 5min << L'\n'; + ss << duration>(3) << L'\n'; + ss << duration>(9) << L'\n'; + std::wstring s; + std::getline(ss, s); + VERIFY( s == L"0s" ); + std::getline(ss, s); + VERIFY( s == L"185min" ); + std::getline(ss, s); + VERIFY( s == L"3[2]s" ); + std::getline(ss, s); + VERIFY( s == L"9[2/3]s" ); +#endif +} + +int main() +{ + test01(); + test02(); +}