lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master e3aeafa 3/5: Overload operator<<=()


From: Greg Chicares
Subject: [lmi-commits] [lmi] master e3aeafa 3/5: Overload operator<<=()
Date: Sun, 21 Mar 2021 21:10:53 -0400 (EDT)

branch: master
commit e3aeafadaeffad768a55a01db4fa18c228655640
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Overload operator<<=()
---
 et_vector.hpp                  | 21 +++++++++++++++++++++
 et_vector_test.cpp             | 39 +++++++++++++--------------------------
 expression_template_0_test.cpp | 40 ++++++++--------------------------------
 3 files changed, 42 insertions(+), 58 deletions(-)

diff --git a/et_vector.hpp b/et_vector.hpp
index dc9acde..e98855a 100644
--- a/et_vector.hpp
+++ b/et_vector.hpp
@@ -183,4 +183,25 @@ inline auto Eval(Expression<U> const& u)
     return z;
 }
 
+/// A (compound) assignment operator.
+///
+/// It is forbidden to intrude a copy-assignment operator into the
+/// std::vector template, but compound assignment operators need not
+/// be members.
+///
+/// Rationale for choosing operator<<=():
+///  - std::vector::operator<<=() is hardly ever used;
+///  - it's so rare that first-time users will realize they should
+///    look for the present documentation;
+///  - it's an assignment operator, with very low precedence; and
+///  - '<<' is reminiscent of stream inserters, which transfer values
+///    from one place to another; but '=' clearly indicates that this
+///    isn't a stream operation.
+
+template<typename T, typename U>
+inline std::vector<T>& operator<<=(std::vector<T>& t, Expression<U> const& u)
+{
+    return t = Eval(u);
+}
+
 #endif // et_vector_hpp
diff --git a/et_vector_test.cpp b/et_vector_test.cpp
index 8a86b09..881ca3d 100644
--- a/et_vector_test.cpp
+++ b/et_vector_test.cpp
@@ -21,30 +21,11 @@
 
 #include "et_vector.hpp"
 
+#include "ssize_lmi.hpp"
 #include "test_tools.hpp"
 
 #include <functional>                   // multiplies(), negate(), plus()
 
-// Experimental operators.
-//
-// See 'expression_template_0_test.cpp' for an earlier implementation
-// of operator<<().
-
-template<typename T, typename U>
-inline std::vector<T>& operator<<(std::vector<T>& t, Expression<U> const& u)
-{
-#if defined PETE_ALLOW_SCALAR_SHIFT
-#   error PETE_ALLOW_SCALAR_SHIFT must not be defined.
-#endif // defined PETE_ALLOW_SCALAR_SHIFT
-    return assign(t, u);
-}
-
-template<typename T, typename U>
-inline std::vector<T>& operator<<=(std::vector<T>& t, Expression<U> const& u)
-{
-    return t = Eval(u);
-}
-
 int test_main(int, char*[])
 {
     {
@@ -86,19 +67,25 @@ int test_main(int, char*[])
     LMI_TEST(r1 == y);
     }
 
-    // Test experimental operator<<() and operator<<=().
+    // Test "assignment" operator<<=().
     {
     std::vector<double> v0 = {1.0, 1.25, 1.5};
     std::vector<double> v1 = {0.0, 0.25, 0.5};
-    std::vector<double> x(3);
-//  assign(x, v0 + v1);
-    x << v0 + v1;
-    std::vector<double> y(3);
-    y <<= v0 + v1 + x;
+
+    std::vector<double> w(5); // Error: not of conformable length.
+    char const* s {"Nonconformable lengths: 5 lhs vs. 3 rhs."};
+    LMI_TEST_THROW(assign(w, v0 + v1), std::runtime_error, s);
+
+    std::vector<double> x(3); // Must be of conformable length.
+    assign(x, v0 + v1);
     std::vector<double> const r0 = {1.0, 1.5, 2.0};
     LMI_TEST(r0 == x);
+
+    std::vector<double> y(7); // Needn't be of conformable length.
+    y <<= v0 + v1 + x;
     std::vector<double> const r1 = {2.0, 3.0, 4.0};
     LMI_TEST(r1 == y);
+    LMI_TEST_EQUAL(3, lmi::ssize(y));
     }
 
     // Test peteCast().
diff --git a/expression_template_0_test.cpp b/expression_template_0_test.cpp
index c2f5cf8..94bae31 100644
--- a/expression_template_0_test.cpp
+++ b/expression_template_0_test.cpp
@@ -447,27 +447,6 @@ void time_one_array_length(int length)
     std::cout << std::endl;
 }
 
-/// Syntactic sugar: "assignment" operator for expression templates.
-///
-/// PETE doesn't support this:
-///    std::vector<double> v2 = v0 - v1;
-/// because it's impossible to intrude a copy-assignment operator into
-/// the standard class template.
-///
-/// operator^=() or operator<<=() would probably be better, but would
-/// require changing the PETE sources, whereas the normally-undefined
-/// macro PETE_ALLOW_SCALAR_SHIFT leaves operator<<() available without
-/// any such change. But is this syntactic sugar sweet enough?
-
-template<typename T, typename V>
-std::vector<T>& operator<<(std::vector<T>& t, V v)
-{
-#if defined PETE_ALLOW_SCALAR_SHIFT
-#   error PETE_ALLOW_SCALAR_SHIFT must not be defined.
-#endif // defined PETE_ALLOW_SCALAR_SHIFT
-    return assign(t, v);
-}
-
 /// Assigning PETE expressions to a std::vector
 ///
 /// std::vector<>::operator= is necessarily a member function, and
@@ -483,25 +462,22 @@ void test_pete_assignment()
     std::vector<double> const v0 = {1.1, 2.2, 3.3, 4.4, 5.5};
     std::vector<double> const v1 = {0.1, 0.2, 0.3, 0.4, 0.5};
     std::vector<double> const v2 = {1.0, 2.0, 3.0, 4.0, 5.0};
-// With the operator<<() above, this:
+// With lmi's overload of operator<<=(), this assign() call:
     std::vector<double> v7a(v0.size());
     assign(v7a, v0 - v1);
     LMI_TEST(v2 == v7a);
-// could be written thus:
+// can be written as an operator instead:
     std::vector<double> v7b(v0.size());
-    v7b << v0 - v1;
+    v7b <<= v0 - v1;
     LMI_TEST(v2 == v7b);
 // though these still wouldn't compile:
-//  std::vector<double> v7c << v0 - v1;
+//  std::vector<double> v7c <<= v0 - v1;
 //  std::vector<double> v7d(v0 - v1);
-// and,
+// and, even though this default-constructed vector is of length zero:
     std::vector<double> v7e;
-// although this would compile:
-//  v7e << v0 - v1;
-// it wouldn't do what one might hope--instead, the result would be
-// empty, as PETE diagnoses:
-    LMI_TEST(0 == v7e.size());
-    LMI_TEST_THROW(v7e << v0 - v1, std::runtime_error, "");
+// this just works (the result has the intended size):
+    v7e <<= v0 - v1;
+    LMI_TEST(v0.size() == v7e.size());
 
 // On the other hand, this syntax is almost natural, even though it's
 // silly to add zero to everything.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]