What is the Matrix - Part 1 - Basic Features

this article was originally posted on CodeGuru in 2003 and has been copied here for archival purposes (and to fix some broken links and tidy up the code formatting)

What is the Matrix? #

The Matrix is a small C++ utility class that is distributed in a single header file. It simplifies working with arrays. Source code and a small demo project in GitHub.

This is the first of three articles that explains how to use the different features of the Matrix. In this article, I will explain the most basic features.

The following code shows how to create a basic, two-dimensional array

CMatrix<int> matrix;
matrix.cell(0, 0) = 100;
matrix.cell(1, 0) = 200;
matrix.cell(0, 1) = 300;
matrix.cell(1, 1) = 400;

You now have a matrix that looks like this:

    100 200
300 400

The Matrix is now a two-dimensional array that contains four cells. The following code shows how to retrieve the values:

int n1 = m.cell(0, 0); //n1 contains 100
int n2 = m.cell(1, 0); //n2 contains 200
int n3 = m.cell(0, 1); //n3 contains 300
int n4 = m.cell(1, 1); //n4 contains 400

The orientation of the matrix is from the top left and indexed by (zero-based) X, Y. So, to get the contents of the 10th cell across and the 5th cell down, you would use cell(10,5).

Pretty simple stuff and not much reason to start using the Matrix. Hopefully, what follows will explain how you can save a lot of coding by using the Matrix.

Templates #

The matrix is a template utility class. The code below demonstrates its use with strings:

CMatrix<CString> matrix;

matrix.cell(0,0) = "string@0,0";
matrix.cell(1,0) = "string@1,0";
matrix.cell(0,1) = "string@0,1";
matrix.cell(1,1) = "string@1,1";

CString s1 = m.cell(0,0); // s1=string@0,0
CString s3 = m.cell(0,1); // s3=string@0,1

//and so on...

Of course, being a template, it can contain other, far more useful types, and can assist with the complexities of those type. For instance, the following...

The MATRIX and COM #

Because matrix is a template utility class, you can store almost anything in them, including VARIANTs.
Those of you who've created SAFEARRAYs know how tedious they are to create and manipulate. This is probably the biggest benefit of using this library because it takes away a lot of the complexity of creating arrays for marshalling with COM. The reams of code below demonstrate everything needed just to create a simple 2D array using the COM API and read the values back out of it:

SAFEARRAYBOUND saBound[ 2 ] ;
saBound[ 0 ].cElements = 2 ; saBound[ 0 ].lLbound = 0 ;
saBound[ 1 ].cElements = 2 ; saBound[ 1 ].lLbound = 0 ;
SAFEARRAY FAR* psa = SafeArrayCreate( VT_VARIANT, 2 , saBound ) ;

_variant_t v1 = 1L, v2 = 2L, v3 = 3L, v4 = 4L;

long idx[2] ;
idx[0] = 0 ; idx[1] = 0;
HRESULT hr = SafeArrayPutElement(psa, idx , &v1) ;

idx[ 0 ] = 0 ; idx[ 1 ] = 1;
hr = SafeArrayPutElement( psa , idx , &v2 ) ;

idx[ 0 ] = 1 ; idx[ 1 ] = 0;
hr = SafeArrayPutElement( psa , idx , &v3 ) ;

idx[ 0 ] = 1 ; idx[ 1 ] = 1;
hr = SafeArrayPutElement( psa , idx , &v4 ) ;

VARIANT v ; VariantInit(&v);
v.parray = psa ; v.vt = VT_ARRAY | VT_VARIANT ;

// now get the stuff back out
long xLow, xHigh, yLow, yHigh, ix[2] ;
xLow = xHigh = yLow = yHigh = ix[0] = ix[1] = 0 ;
SafeArrayGetUBound ( v.parray, 1, &xHigh ) ;
SafeArrayGetLBound (v.parray , 1 , &xLow ) ;

SafeArrayGetUBound ( v.parray, 2, &yHigh ) ;
SafeArrayGetLBound( v.parray , 2 , &yLow ) ;

_variant_t vcell1, vcell2,vcell3, vcell4 ;

ix[0] = 0 ; ix[1] = 0 ;
hr = SafeArrayGetElement( v.parray, ix, &vcell1 ) ;

ix[0] = 0 ; ix[1] = 1 ;
hr = SafeArrayGetElement( v.parray, ix, &vcell2 ) ;

ix[0] = 1 ; ix[1] = 0 ;
hr = SafeArrayGetElement( v.parray, ix, &vcell3 ) ;

ix[0] = 1 ; ix[1] = 1 ;
hr = SafeArrayGetElement( v.parray, ix, &vcell4 ) ;

cout <<
"vcell1=" << vcell1.iVal << "\n" <<
"vcell2=" << vcell2.iVal << "\n" <<
"vcell3=" << vcell3.iVal << "\n" <<
"vcell4=" << vcell4.iVal << "\n" <<

"press enter to continue";

cin.get();

As you can see, this code is quite convoluted. Using this library to achieve the exactly the same result looks like this:

_variant_t v1 = 1L, v2 = 2L, v3 = 3L, v4 = 4L;

CMatrix<_variant_t> matrix ;
matrix.cell(0,0)=v1 ;
matrix.cell(0,1)=v2 ;
matrix.cell(1,0)=v3;
matrix.cell(1,1)=v4;

VARIANT vIn = matrix;

// now get the stuff back out

_variant_t vcell1, vcell2, vcell3, vcell4;
CMatrix<_variant_t> m ;
m=vIn ;
vcell1 = m.cell(0,0);
vcell2 = m.cell(0,1);
vcell3 = m.cell(1,0);
vcell4 = m.cell(1,1);

cout <<
"vcell1=" << vcell1.iVal << "\n" <<
"vcell2=" << vcell2.iVal << "\n" <<
"vcell3=" << vcell3.iVal << "\n" <<
"vcell4=" << vcell4.iVal << "\n" <<

"press enter to continue";

cin.get();

The above example creates a Matrix and then creates a VARIANT SAFEARRAY from the Matrix. The items that are written to the screen are from another Matrix created from the VARIANT SAFEARRAY. To simplify things yet further, the code below does exactly the same thing:

CMatrix<_variant_t> matrix ;
matrix.cell(0,0) = 1L ;
matrix.cell(0,1) = 2L ;
matrix.cell(1,0) = 3L;
matrix.cell(1,1) = 4L;

VARIANT vIn=matrix;

// now get the stuff back out
CMatrix<_variant_t> m(vIn) ;
cout <<
"vcell1=" << m.cell(0,0).iVal << "\n" <<
"vcell2=" << m.cell(0,1).iVal << "\n" <<
"vcell3=" << m.cell(1,0).iVal << "\n" <<
"vcell4=" << m.cell(1,1).iVal << "\n" <<

"press enter to continue";

cin.get();

Conclusion #

Hopefully, this article has shown you how simple it is to use the Matrix class and how much coding it takes away when dealing with arrays. In the next article, I will discuss how to use the Matrix class to marshal SAFEARRAYs in COM.

🙏🙏🙏

Since you've made it this far, sharing this article on your favorite social media network would be highly appreciated 💖! For feedback, please 🦋 ping me on Bluesky! 🦋

Leave a comment

Comments are moderated, so there may be a short delays before you see it.

Published