From bb4854fca80cc20f0ebd60bfae2557d7c754073e Mon Sep 17 00:00:00 2001
From: Recolic Keghart <root@recolic.net>
Date: Wed, 11 Aug 2021 10:54:18 +0000
Subject: [PATCH] bug fix for rlib::printable_iter, to allow object that only
 has nonconst begin() to get printable_iter

---
 require/cxx20      | 12 ++++++++++++
 stdio.hpp          | 49 +++++++++++++++++++++++++++++-----------------
 test/src/common.cc |  7 ++++++-
 3 files changed, 49 insertions(+), 19 deletions(-)
 create mode 100644 require/cxx20

diff --git a/require/cxx20 b/require/cxx20
new file mode 100644
index 0000000..8202250
--- /dev/null
+++ b/require/cxx20
@@ -0,0 +1,12 @@
+#ifndef R_CXX20_REQUIRED
+#define R_CXX20_REQUIRED
+
+#include <rlib/sys/os.hpp>
+
+#if RLIB_CXX_STD <= 2017
+#error This file requires compiler and library support \
+for the ISO C++ 2020 standard. This support must be enabled \
+with the -std=c++20 or -std=c++2a compiler options.
+#endif
+
+#endif
diff --git a/stdio.hpp b/stdio.hpp
index cdc028a..ced54db 100644
--- a/stdio.hpp
+++ b/stdio.hpp
@@ -102,19 +102,22 @@ namespace rlib {
         template <typename Iterable, typename Printable>
         struct _printable_iterable : private std::pair<Iterable, Printable> {
             using std::pair<Iterable, Printable>::pair;
-            const Iterable &arg() const {return std::pair<Iterable, Printable>::first;}
-            const Printable &spliter() const {return std::pair<Iterable, Printable>::second;}
+            using _printable_iterable_tag = void;
+            Iterable &arg() & {return std::pair<Iterable, Printable>::first;}
+            Printable &spliter() & {return std::pair<Iterable, Printable>::second;}
+            Iterable &&arg() && {return std::pair<Iterable, Printable>::first;}
+            Printable &&spliter() && {return std::pair<Iterable, Printable>::second;}
+            const Iterable &arg() const & {return std::pair<Iterable, Printable>::first;}
+            const Printable &spliter() const & {return std::pair<Iterable, Printable>::second;}
         };
+        template <typename FirstT, typename SecondT> using FirstOf = FirstT;
     }
 
-    // 2 more interfaces...
-    template <typename Iterable, typename Printable>
-    const impl::_printable_iterable<Iterable, Printable> printable_iter(Iterable arg, Printable spliter) {
-        return impl::_printable_iterable<Iterable, Printable>(arg, spliter);
-    }
-    template <typename Iterable>
-    const impl::_printable_iterable<Iterable, char> printable_iter(Iterable arg) {
-        return impl::_printable_iterable<Iterable, char>(arg, ' ');
+    // more interfaces...
+    template <typename Iterable, typename Printable = char>
+    auto printable_iter(Iterable &&arg, Printable spliter = ' ') -> impl::_printable_iterable<typename std::decay<Iterable>::type, Printable> {
+        // TODO: avoid the extra copy while passing lvalue reference on constructing return value obj. 
+        return impl::_printable_iterable<typename std::decay<Iterable>::type, Printable>(std::forward<Iterable>(arg), spliter);
     }
 
     inline bool sync_with_stdio(bool sync = true) noexcept {
@@ -124,8 +127,8 @@ namespace rlib {
         return impl::enable_endl_flush() = enable;
     }
 
-// Implements.
-    template < class CharT, class Traits >
+    // Implements below ---------------------
+    template <typename CharT, typename Traits>
     inline std::basic_ostream<CharT, Traits>& endl(std::basic_ostream<CharT, Traits>& os) {
         os << RLIB_IMPL_ENDLINE;
         if(impl::enable_endl_flush())
@@ -215,12 +218,22 @@ namespace rlib {
     }
 } // end namespace rlib
 
-template <typename Iterable, typename Printable>
-std::ostream& operator<< (std::ostream& stream, const rlib::impl::_printable_iterable<Iterable, Printable> &p) {
-    for(auto val : p.arg()) {
-        stream << val << p.spliter();
-    }
-    return stream;
+// auto-deduct const/nonconst left/right value ref. 
+// For C++20 std::view, it doesn't have a `begin()` method for const lvalue ref. So we have to support 
+//   passing rvalue from rlib::printable_iter() to rlib::impl::_printable_iterable to operator<< 
+// Instead of writing 3 overloads here, let us deduce automatically. 
+template <typename PrintableIterableT>
+rlib::impl::FirstOf<std::ostream&, typename std::decay<PrintableIterableT>::type::_printable_iterable_tag> operator<< (std::ostream& stream, PrintableIterableT &&p) {
+     for(auto val : p.arg())
+         stream << val << p.spliter();
+     return stream;
 }
+// // Old version, for backup
+// template <typename Iterable, typename Printable>
+// std::ostream& operator<< (std::ostream& stream, rlib::impl::_printable_iterable<Iterable, Printable> &&p) {
+//     for(auto val : p.arg())
+//         stream << val << p.spliter();
+//     return stream;
+// }
 
 #endif
diff --git a/test/src/common.cc b/test/src/common.cc
index 183c976..946908f 100644
--- a/test/src/common.cc
+++ b/test/src/common.cc
@@ -82,7 +82,12 @@ TEST_CASE("stdio.hpp") {
     std::stringstream ss1, ss2;
     rlib::println(ss1, rlib::printable_iter(v, rlib::printable_iter(v)));
     rlib::print(ss2, rlib::printable_iter(v, rlib::printable_iter(v)));
-    rlib::println(ss1.str() == answer + '\n', ss2.str() == answer);
+    rlib::println(ss1.str() == answer + RLIB_IMPL_ENDLINE, ss2.str() == answer);
+
+    const rlib_test_iterable vc{1.2, 6.666, 12, -11.11};
+    std::stringstream ssvc;
+    rlib::println(ssvc, rlib::printable_iter(vc, rlib::printable_iter(vc)));
+    REQUIRE(ssvc.str() == answer + RLIB_IMPL_ENDLINE);
 }
 
 
-- 
GitLab