/* * This acts as an example on how to not introduce temporaries * for evaluating expressions. The problem is related to the * prod() function, where the temps are going out of scope. */ extern "C" int printf(const char*, ...); #ifndef restrict #define restrict __restrict__ #endif template class Matrix; struct XprNull { explicit XprNull() { } }; static inline double operator+(const double& lhs, XprNull) { return lhs; } struct fcnl_Assign { static inline void applyOn(double& restrict lhs, double rhs) { lhs = rhs; } }; template struct MetaMatrix { enum { doRows = (RowStride < Rows - 1) ? 1 : 0, doCols = (ColStride < Cols - 1) ? 1 : 0 }; template static inline void assign2(Mtrx& mat, const E& expr, const Fcnl& fn) { fn.applyOn(mat(RowStride, ColStride), expr(RowStride, ColStride)); MetaMatrix::assign2(mat, expr, fn); } template static inline void assign(Mtrx& mat, const E& expr, const Fcnl& fn) { MetaMatrix::assign2(mat, expr, fn); MetaMatrix::assign(mat, expr, fn); } }; template<> struct MetaMatrix<0, 0, 0, 0> { template static inline void assign2(Mtrx&, const E&, const Fcnl&) { } template static inline void assign(Mtrx&, const E&, const Fcnl&) { } }; template struct MetaGemm { enum { doIt = (K != Cols1 - 1) }; static inline double prod(const double* restrict lhs, const double* restrict rhs, unsigned i, unsigned j) { return lhs[i * RowStride1 + K * ColStride1] * rhs[K * RowStride2 + j * ColStride2] + MetaGemm::prod(lhs, rhs, i, j); } }; template<> struct MetaGemm<0,0,0,0,0,0,0,0> { static inline XprNull prod(const void*, const void*, unsigned, unsigned) { return XprNull(); } }; template struct XprMMProduct { explicit XprMMProduct(const double* restrict lhs, const double* restrict rhs) : m_lhs(lhs), m_rhs(rhs) { } double operator()(unsigned i, unsigned j) const { return MetaGemm::prod(m_lhs, m_rhs, i, j); } private: const double* restrict m_lhs; const double* restrict m_rhs; }; template struct XprMatrixTranspose { explicit XprMatrixTranspose(const E& e) : m_expr(e) { } double operator()(unsigned i, unsigned j) const { return m_expr(j, i); } private: const E& restrict m_expr; }; template struct XprMatrix { explicit XprMatrix(const E& e) : m_expr(e) { } double operator()(unsigned i, unsigned j) const { return m_expr(i, j); } private: const E& restrict m_expr; }; template struct MatrixConstReference { explicit MatrixConstReference(const Matrix& rhs) : m_data(rhs.m_data) { } double operator()(unsigned i, unsigned j) const { return m_data[i * RowStride + j * ColStride]; } private: const double* restrict m_data; }; template struct Matrix { explicit Matrix() { m_data = new double [Rows*Cols]; } template explicit Matrix(const XprMatrix& rhs) { m_data = new double [Rows*Cols]; MetaMatrix::assign(*this, rhs, fcnl_Assign()); } ~Matrix() { delete [] m_data; } double& restrict operator()(unsigned i, unsigned j) { return m_data[i * Cols + j]; } double operator()(unsigned i, unsigned j) const { return m_data[i * Cols + j]; } MatrixConstReference const_ref() const { return MatrixConstReference(*this); } template Matrix& operator=(const XprMatrix& rhs) { MetaMatrix::assign(*this, rhs, fcnl_Assign()); return *this; } void print() const { printf("[\n"); for(unsigned i = 0; i != Rows; ++i) { printf("\t["); for(unsigned j = 0; j != Cols; ++j) printf("\t%+4.2f", this->operator()(i, j)); printf("]\n"); } printf("]\n"); } // private: double* m_data; }; template inline XprMatrix< XprMMProduct< Rows1, Cols1, Cols2, Cols1, 1, Cols2, 1 >, Rows1, Cols2 > prod(const XprMatrix& lhs, const Matrix& rhs) { typedef XprMMProduct< Rows1, Cols1, Cols2, Cols1, 1, Cols2, 1 > expr_type; Matrix temp_lhs(lhs); return XprMatrix(expr_type(temp_lhs.m_data, rhs.m_data)); } template inline XprMatrix< XprMatrixTranspose< MatrixConstReference >, Cols, Rows > trans(const Matrix& rhs) { typedef XprMatrixTranspose< MatrixConstReference > expr_type; return XprMatrix(expr_type(rhs.const_ref())); } /** * Test driver */ using namespace std; int main() { Matrix<3,2> B; Matrix<3,3> D; Matrix<2,2> K; B(0,0) = -0.05; B(0,1) = 0; B(1,0) = 0; B(1,1) = 0.05; B(2,0) = 0.05; B(2,1) = -0.05; D(0,0) = 2000; D(0,1) = 1000; D(0,2) = 0; D(1,0) = 1000; D(1,1) = 2000; D(1,2) = 0; D(2,0) = 0; D(2,1) = 0; D(2,2) = 500; K = prod(prod(trans(B), D), B); printf("K = "); K.print(); // wrong result, should be symetric }