for L to R parsing, compatible A, B and C matrix objects, and a scalar variable d, will implicitly generate two temporaries:
temporary1 = B + C temporary2 = d*temporary1 A = temporary2
If the dimensions of B and C are large, the evaluation performance of (E1) will be degraded substantially due to the allocation, deallocation and copying of large memory blocks.
Ideally, one expects that (E1) and any expression, in general, no matter how complex it is, would be substituted by the compiler with the following snippet:
Matrix<double> A, B, C; double d; ... for(Matrix<double>::iterator it = A.begin(); it != A.end(); it++) A[i] = d*(B[i] + C[i]);
This is exactly achieved by expression-templates and proper inlining of the arithmetic operations involved in the expression.
We may further expand the above example, to generators for any analytic operation. For example, using an expression template on a vector with 100 elements:
vector<float> v(100, 1); for(vector<float>::size_type i = 0; i < v.size(); i++) v[i] = 2*M_PI*i/100;
the statement<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0 ,>
is substituted at compile-time by:
for(vector<float>::size_type i = 0; i < v.size(); i++) v[i] = sin(v[i]);
and a complex statement like:<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0 ,>
vector<float> u = Sin(v) + 2*w + z;
for(size_t i = 0; i < v.size(); i++) v[i] = sin(v[i]) + 2.f*w[i] + z[i];
The same programming paradigm is implemented in the lambda-calculus package of Boost library. There, the purpose is to be able to write statements like:
vector<float> v, u, w; ... boost::for_each(IteratorFirst, IteratorLast, v = 2.f*u + w/2.f);
again eliminating temporaries.
Code usage is straightforward as can be seen in the .cpp file I created for testing the library. The following are worth noting:
The CPP file uses the library on a derivative of
std::vector template. The derivation is very easy as can be seen in Vector.h header. Eventually, it adds only a public attribute, an expression object pointing to the beginning of the container, and provides a new assignment operator and a Find method that returns the result of arithmetic and logical operations, respectively. Logical operations always return a
vector<size_t> object with the indices of vector elements satisfying the logical operation.
Arithmetic and logical operations are performed using the attribute
e of type
E<I<iterator> > of
Vector class. The attribute is simply assigned the
begin() return value of the base class
vector<...>. I have deliberately chosen to do so for clarity. Then, E2, for example, must be written as:
Writing E4 as E2 can be implemented very easily by rewriting
Sin to accept as argument a
Vector type and constructing the expression object internally. The following fragments from the test project define two vectors of 100 elements each. The values are taken to represent angles in radians equal to 1/100th of the circumference of a circle.
// Define vectors with 100 elements each and initial value 0 DoubleVector v1(100, 0), v2(100, 0); // assign angle values to v1 for(size_t i = 0; i < v1.size(); i++) v1[i] = 2*M_PI*i/100; // Arbitrary arithmetic computations on v1 and v2 v1 = Sin(v1.e) + Cos(v1.e); v2 = 1. + Exp(-v1.e) + v1.e/2.;
Logical operations return an
std::vector<size_t> with the indices of vector elements satisfying the expression. For example:
vector<size_t> idxs = v2.Find(v1.e > 0.);
The gist of the templates contained in "Expressions.h" header are the I and It identity templates – representing terminal nodes in the expression tree, the BE and UE templates – representing non-terminal binary and unary operations in the expression tree, and the E template that acts as a base class in the OO terminology.
The type an expression object actually represents is resolved by the
typedef, present in all classes. The expression-evaluation recursion step is implemented by
Satisfies methods, defined in the derivatives of I, It, BE and UE templates.
Last but not least: notice that the iterative evaluation of the expression over the elements of the composite Vector template is done by the assignment operator. The mere role of the whole template library is to parse inline the expression being evaluated.
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here