//[ TArray /////////////////////////////////////////////////////////////////////////////// // Copyright 2008 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // This example constructs a mini-library for linear algebra, using // expression templates to eliminate the need for temporaries when // adding arrays of numbers. It duplicates the TArray example from // PETE (http://www.codesourcery.com/pooma/download.html) #include #include #include #include namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; // This grammar describes which TArray expressions // are allowed; namely, int and array terminals // plus, minus, multiplies and divides of TArray expressions. struct TArrayGrammar : proto::or_< proto::terminal< int > , proto::terminal< int[3] > , proto::plus< TArrayGrammar, TArrayGrammar > , proto::minus< TArrayGrammar, TArrayGrammar > , proto::multiplies< TArrayGrammar, TArrayGrammar > , proto::divides< TArrayGrammar, TArrayGrammar > > {}; template struct TArrayExpr; // Tell proto that in the TArrayDomain, all // expressions should be wrapped in TArrayExpr<> and // must conform to the TArrayGrammar struct TArrayDomain : proto::domain, TArrayGrammar> {}; // Here is an evaluation context that indexes into a TArray // expression, and combines the result. struct TArraySubscriptCtx : proto::callable_context< TArraySubscriptCtx const > { typedef int result_type; TArraySubscriptCtx(std::ptrdiff_t i) : i_(i) {} // Index array terminals with our subscript. Everything // else will be handled by the default evaluation context. int operator ()(proto::tag::terminal, int const (&data)[3]) const { return data[this->i_]; } std::ptrdiff_t i_; }; // Here is an evaluation context that prints a TArray expression. struct TArrayPrintCtx : proto::callable_context< TArrayPrintCtx const > { typedef std::ostream &result_type; TArrayPrintCtx() {} std::ostream &operator ()(proto::tag::terminal, int i) const { return std::cout << i; } std::ostream &operator ()(proto::tag::terminal, int const (&arr)[3]) const { return std::cout << '{' << arr[0] << ", " << arr[1] << ", " << arr[2] << '}'; } template std::ostream &operator ()(proto::tag::plus, L const &l, R const &r) const { return std::cout << '(' << l << " + " << r << ')'; } template std::ostream &operator ()(proto::tag::minus, L const &l, R const &r) const { return std::cout << '(' << l << " - " << r << ')'; } template std::ostream &operator ()(proto::tag::multiplies, L const &l, R const &r) const { return std::cout << l << " * " << r; } template std::ostream &operator ()(proto::tag::divides, L const &l, R const &r) const { return std::cout << l << " / " << r; } }; // Here is the domain-specific expression wrapper, which overrides // operator [] to evaluate the expression using the TArraySubscriptCtx. template struct TArrayExpr : proto::extends, TArrayDomain> { typedef proto::extends, TArrayDomain> base_type; TArrayExpr( Expr const & expr = Expr() ) : base_type( expr ) {} // Use the TArraySubscriptCtx to implement subscripting // of a TArray expression tree. int operator []( std::ptrdiff_t i ) const { TArraySubscriptCtx const ctx(i); return proto::eval(*this, ctx); } // Use the TArrayPrintCtx to display a TArray expression tree. friend std::ostream &operator <<(std::ostream &sout, TArrayExpr const &expr) { TArrayPrintCtx const ctx; return proto::eval(expr, ctx); } }; // Here is our TArray terminal, implemented in terms of TArrayExpr // It is basically just an array of 3 integers. struct TArray : TArrayExpr< proto::terminal< int[3] >::type > { explicit TArray( int i = 0, int j = 0, int k = 0 ) { (*this)[0] = i; (*this)[1] = j; (*this)[2] = k; } // Here we override operator [] to give read/write access to // the elements of the array. (We could use the TArrayExpr // operator [] if we made the subscript context smarter about // returning non-const reference when appropriate.) int &operator [](std::ptrdiff_t i) { return proto::value(*this)[i]; } int const &operator [](std::ptrdiff_t i) const { return proto::value(*this)[i]; } // Here we define a operator = for TArray terminals that // takes a TArray expression. template< typename Expr > TArray &operator =(Expr const & expr) { // proto::as_expr(expr) is the same as // expr unless expr is an integer, in which case it // is made into a TArrayExpr terminal first. return this->assign(proto::as_expr(expr)); } template< typename Expr > TArray &printAssign(Expr const & expr) { *this = expr; std::cout << *this << " = " << expr << std::endl; return *this; } private: template< typename Expr > TArray &assign(Expr const & expr) { // expr[i] here uses TArraySubscriptCtx under the covers. (*this)[0] = expr[0]; (*this)[1] = expr[1]; (*this)[2] = expr[2]; return *this; } }; int main() { TArray a(3,1,2); TArray b; std::cout << a << std::endl; std::cout << b << std::endl; b[0] = 7; b[1] = 33; b[2] = -99; TArray c(a); std::cout << c << std::endl; a = 0; std::cout << a << std::endl; std::cout << b << std::endl; std::cout << c << std::endl; a = b + c; std::cout << a << std::endl; a.printAssign(b+c*(b + 3*c)); return 0; } //]