[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.