Implementing a Constant-Sized Matrix

Problem

You want an efficient matrix implementation where the dimensions (i.e., number of rows and columns) are constants known at compile time.

Solution

When the dimensions of a matrix are known at compile time, the compiler can more easily optimize an implementation that accepts the row and columns as template parameters as shown in Example 11-30.

Example 11-30. kmatrix.hpp

#ifndef KMATRIX_HPP
#define KMATRIX_HPP

#include "kvector.hpp"
#include "kstride_iter.hpp"

template
class kmatrix
{
public:
 // public typedefs
 typedef Value_T value_type;
 typedef kmatrix self;
 typedef Value_T* iterator;
 typedef const Value_T* const_iterator;
 typedef kstride_iter row_type;
 typedef kstride_iter col_type;
 typedef kstride_iter const_row_type;
 typedef kstride_iter const_col_type;

 // public constants
 static const int nRows = Rows_N;
 static const int nCols = Cols_N;

 // constructors
 kmatrix( ) { m = Value_T( ); }
 kmatrix(const self& x) { m = x.m; }
 explicit kmatrix(Value_T& x) { m = x.m; }

 // public functions
 static int rows( ) { return Rows_N; }
 static int cols( ) { return Cols_N; }
 row_type row(int n) { return row_type(begin( ) + (n * Cols_N)); }
 col_type col(int n) { return col_type(begin( ) + n); }
 const_row_type row(int n) const {
 return const_row_type(begin( ) + (n * Cols_N));
 }
 const_col_type col(int n) const {
 return const_col_type(begin( ) + n);
 }
 iterator begin( ) { return m.begin( ); }
 iterator end( ) { return m.begin( ) + size( ); }
 const_iterator begin( ) const { return m; }
 const_iterator end( ) const { return m + size( ); }
 static int size( ) { return Rows_N * Cols_N; }

 // operators
 row_type operator[](int n) { return row(n); }
 const_row_type operator[](int n) const { return row(n); }

 // assignment operations
 self& operator=(const self& x) { m = x.m; return *this; }
 self& operator=(value_type x) { m = x; return *this; }
 self& operator+=(const self& x) { m += x.m; return *this; }
 self& operator-=(const self& x) { m -= x.m; return *this; }
 self& operator+=(value_type x) { m += x; return *this; }
 self& operator-=(value_type x) { m -= x; return *this; }
 self& operator*=(value_type x) { m *= x; return *this; }
 self& operator/=(value_type x) { m /= x; return *this; }
 self operator-( ) { return self(-m); }

 // friends
 friend self operator+(self x, const self& y) { return x += y; }
 friend self operator-(self x, const self& y) { return x -= y; }
 friend self operator+(self x, value_type y) { return x += y; }
 friend self operator-(self x, value_type y) { return x -= y; }
 friend self operator*(self x, value_type y) { return x *= y; }
 friend self operator/(self x, value_type y) { return x /= y; }
 friend bool operator==(const self& x, const self& y) { return x != y; }
 friend bool operator!=(const self& x, const self& y) { return x.m != y.m; }
private:
 kvector m;
};

#endif

Example 11-31 shows a program that demonstrates how to use the kmatrix template class.

Example 11-31. Using kmatrix

#include "kmatrix.hpp"

#include 

using namespace std;

template
void outputRowOrColumn(Iter_T iter, int n) {
 for (int i=0; i < n; ++i) {
 cout << iter[i] << " ";
 }
 cout << endl;
}

template
void initializeMatrix(Matrix_T& m) {
 int k = 0;
 for (int i=0; i < m.rows( ); ++i) {
 for (int j=0; j < m.cols( ); ++j) {
 m[i][j] = k++;
 }
 }
}

template
void outputMatrix(Matrix_T& m) {
 for (int i=0; i < m.rows( ); ++i) {
 cout << "Row " << i << " = ";
 outputRowOrColumn(m.row(i), m.cols( ));
 }
 for (int i=0; i < m.cols( ); ++i) {
 cout << "Column " << i << " = ";
 outputRowOrColumn(m.col(i), m.rows( ));
 }
}

int main( )
{
 kmatrix m;
 initializeMatrix(m);
 m *= 2;
 outputMatrix(m);
}

The program in Example 11-31 produces the following output:

Row 0 = 0 2 4 6
Row 1 = 8 10 12 14
Column 0 = 0 8
Column 1 = 2 10
Column 2 = 4 12
Column 3 = 6 14

 

Discussion

This design and usage for the kmatrix class template in Example 11-30 and Example 11-31 is very similar to the matrix class template presented in Recipe 11.14. The only significant difference is that to declare an instance of a kmatrix you pass the dimensions as template parameters, as follows:

kmatrix m; // declares a matrix with five rows and six columns

It is common for many kinds of applications requiring matricies that the dimensions are known at compile-time. Passing the row and column size as template parameters enables the compiler to more easily apply common optimizations such as loop-unrolling, function inlining, and faster indexing.

Like the constant-sized vector template presented earlier (kvector), the kmatrix template is particularly effective when using small matrix sizes.

 

See Also

Recipe 11.14 and Recipe 11.16

Building C++ Applications

Code Organization

Numbers

Strings and Text

Dates and Times

Managing Data with Containers

Algorithms

Classes

Exceptions and Safety

Streams and Files

Science and Mathematics

Multithreading

Internationalization

XML

Miscellaneous

Index



C++ Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2006
Pages: 241

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net