PETE: The Portable Expression Template Engine by Scott Haney, James Crotinger, Steve Karmesin, Stephen Smith Example 1: vector::iterator iterA = A.begin(); vector::const_iterator iterB = B.begin(); vector::const_iterator iterC = C.begin(); while (iterA != A.end()) { *iterA += -*iterB + 2 * *iterC; ++iterA; ++iterB; ++iterC; } Example 2: classes ----- ARG = "class T[n], class Allocator[n]" CLASS = "vector" Example 3: template inline vector &operator+=(vector &lhs, const Expression &rhs) { evaluate(lhs, OpAddAssign(), rhs); return lhs; } Example 4: classes ----- ARG = "class T[n], class Allocator[n]" CLASS = "vector" ----- ARG = "class T[n], class Allocator[n]" CLASS = "list" Listing One #include #include #include "PETE/PETE.h" #include "Listing1Operators.h" // We need to specialize the CreateLeaf traits class for STL vectors so that // operators know what to stick in the leaves of the expression tree. In this // case, we store a const_iterator at the leaves. template struct CreateLeaf > { typedef typename vector::const_iterator Leaf_t; inline static Leaf_t make(const vector &a) { return a.begin(); } }; // Loop over vector and evaluate the expression at each location. template inline void evaluate(vector &lhs, const AssignOp &op, const RHS &rhs) { typename vector::iterator iterLHS = lhs.begin(); while (iterLHS != lhs.end()) { // The actual assignment operation is performed here. PETE operator // tags all define operator() to perform the operation. (In this case // op performs an assignment operation.) forEach is used to compute // the rhs value. DereferenceLeaf gets the values at each node by // deferencing an iterator, and the tag OpCombine tells forEach to use // the operator tags in the expression to combine values together. op(*iterLHS, forEach(rhs, DereferenceLeaf(), OpCombine())); // Increment the LHS iterator. iterLHS++; // Now, we have to increment the iterators for everything on the RHS. // The results don't need to be combined so we use a NullCombiner. forEach(rhs, IncrementLeaf(), NullCombine()); } } int main() { int i; const int n = 10; vector A, C, E(n); vector B, D; for (i = 0; i < n; ++i) { A.push_back(i); B.push_back(2*i); C.push_back(3*i); D.push_back(i); } A += -B + 2 * C; assign(B, 2); assign(D, A + B * C); A += where(D < 30, B, C); assign(E, C); E += E - 4 / (sin(C) + 1); for (i = 0; i < n; ++i) { cout << " A[" << i << "] = " << A[i] << " B[" << i << "] = " << B[i] << " C[" << i << "] = " << C[i] << " D[" << i << "] = " << D[i] << " E[" << i << "] = " << E[i] << endl; } return 0; } Listing Two #include #include #include #include "PETE/PETE.h" #include "Listing2Operators.h" // We need to specialize the CreateLeaf traits class for STL vectors and lists // so that operators know what to stick in the leaves of the expression tree. // In these cases, we store const_iterators at the leaves. template struct CreateLeaf > { typedef typename vector::const_iterator Leaf_t; inline static Leaf_t make(const vector &a) { return a.begin();} }; template struct CreateLeaf > { typedef typename list::const_iterator Leaf_t; inline static Leaf_t make(const list &a) { return a.begin(); } }; // Loop over vector and evaluate the expression at each location. template inline void evaluate(vector &lhs, const Op &op, const RHS &rhs) { for (int i = 0; i < lhs.size(); ++i) { // The actual assignment operation is performed here. // PETE operator tags all define operator() to perform the operation. // (In this case op performs an assignment operation.) // forEach is used to compute the rhs value. DereferenceLeaf gets the // values at each node by deferencing an iterator, and the tag // OpCombine tells forEach to use the operator tags in the expression // to combine values together. op(lhs[i], forEach(rhs, DereferenceLeaf(), OpCombine())); // Now, we have to increment the iterators for everything on the RHS. // The results don't need to be combined so we use a NullCombiner. forEach(rhs, IncrementLeaf(), NullCombine()); } } // Loop over list and evaluate the expression at each location. template inline void evaluate(list &lhs, const Op &op, const RHS &rhs) { typename list::iterator i = lhs.begin(); while (i != lhs.end()) { // The actual assignment operation is performed here. // PETE operator tags all define operator() to perform the operation. // (In this case op performs an assignment operation.) // forEach is used to compute the rhs value. DereferenceLeaf gets the // values at each node by deferencing an iterator, and the tag // OpCombine tells forEach to use the operator tags in the expression // to combine values together. op(*i++, forEach(rhs, DereferenceLeaf(), OpCombine())); // Now, we have to increment the iterators for everything on the RHS. // The results don't need to be combined so we use a NullCombiner. forEach(rhs, IncrementLeaf(), NullCombine()); } } int main() { int i; const int n = 10; vector A, C; vector B; list D; list E; for (i = 0; i < n; ++i) { A.push_back(i); B.push_back(2*i); C.push_back(3*i); D.push_back(i); E.push_back(0.0); } A += -B + 2 * C; assign(B, 2); assign(D, A + B * C); A += where(D < 30, B, C); assign(E, C); E += E - 4 / (sin(C) + 1); list::const_iterator di = D.begin(); list::const_iterator ei = E.begin(); for (i = 0; i < n; ++i) { cout << " A[" << i << "] = " << A[i] << " B[" << i << "] = " << B[i] << " C[" << i << "] = " << C[i] << " D[" << i << "] = " << *di++ << " E[" << i << "] = " << *ei++ << endl; } return 0; } 5