This is a missing piece of the C++20 <chrono> header.

It would be good to move the code into the compiled library, so that we
don't need <sstream> in <chrono>. 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.

Tested powerpc64le-linux. Committed to trunk.

commit fcc13d6fc31441b5672b68a5e3b247687724218f
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Thu Oct 7 19:58:07 2021

    libstdc++: Implement ostream insertion for chrono::duration
    
    This is a missing piece of the C++20 <chrono> header.
    
    It would be good to move the code into the compiled library, so that we
    don't need <sstream> in <chrono>. 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 <bits/chrono.h>
+#if __cplusplus > 201703L
+# include <sstream> // ostringstream
+# include <bits/charconv.h>
+#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<typename _Period>
+       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<typename _Period, typename _CharT>
+       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<typename _CharT, typename _Traits,
+            typename _Rep, typename _Period>
+      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<intmax_t>::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<period, _CharT>(__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 <chrono>
+#include <sstream>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  using namespace std::chrono;
+  std::stringstream ss;
+  ss << 0s << '\n';
+  ss << 3h + 5min << '\n';
+  ss << duration<long, std::ratio<2>>(3) << '\n';
+  ss << duration<long, std::ratio<2, 3>>(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<long, std::ratio<2>>(3) << L'\n';
+  ss << duration<long, std::ratio<2, 3>>(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();
+}

Reply via email to