Back Table of content Next

More technical stuff

const variables and reference arguments

The Matrix and Vector classes are in fact wrappers around a private ReferencedData class, that actually hold the data pointer itself. This has consequences over the semantics of the const keyword, since in code like:

const Matrix a(10,10);

the constness is only for the wrapper, not the referenced data. For example, the following

a(5,5) = 3.5;

is perfectly valid since a itself isn't modified. A similar ability is also used extensively for temporaries. As you know, taking a non-const reference to a temporary is usually a bad idea. But, thanks to this wrapper property, you can make all arguments const references and use shallow copies for the return values, which makes the code faster:

Matrix myfunc(const Matrix& m);

This function could act on the data of m and modify it safely, though it could very well be useless (see below). It cannot, on the other hand, modify the dimensions of m (or similar operations that don't act only on data). It is best to return a copy, that is a Matrix and not a Matrix&, since otherwise there is no guarantee the reference you return will be valid after the function ends, unless you take care of that too. Returning a shallow copy is quite fast, and much safer: as the local object dies its data does not, since the returned copy still hold one valid reference to it.
All this also means that you can invoke:

a = myfunc(b.transpose()+c);

and similar code without worrying about temporaries not being valid. Matrix temporaries will be created, but since all arguments are const references it doesn't matter. The data the matrices refer to, on the other hand, is reference counted and so will be valid so long as there is one object, temporary or not, that refers to it. With this example, you can also notice it is generally useless to modify the matrix in argument, since it could very well be a temporary (see the 'load' utility function for a useful counter-example).
In short, you can just remember the following rules: const reference arguments and plain return values are best, and also don't modify your argument but return instead a modified (shallow) copy. Those two rules are applied for all Matrix member functions, so that reshape for example is a const function (i.e. that does not modify the object it refers to) and return the reshaped version (without actually copying data, of course). And if you worry about the memory, you can always invoke the Matrix::temporary function to let the user decide the swapping policy of the temporaries (see the documentation for this function).

The mapper functionals

By functionals I mean all the familly of map functions, together with generate and the fill member method. The common point for all those is they can take a function as an argument, like:

b = map(dosomething, a, 3.0);

This would successively call dosomething(arg, 3.0) on each cell arg of a and assign the resulting matrix to b. The thing is, in the previous example, dosomething is a function of type MatrixType (*)(MatrixType, MatrixType) where MatrixType is typedef'd to double by default.
I just wanted to remind you this excludes class methods, that have a hidden 'this' argument. There are ways around this, more or less hacks actually. For convenience, a void * argument can also be passed. But beware, void * isn't (always?) a valid base-class-with-virtuals pointer type, so you have to exercise great care if you still wish to map wrappers around class methods. You could, I suppose, pass a struct pointer, which struct would in turn contain a well-defined base class pointer, but then you may like to rethink about you code a little bit.
Another point, which I didn't do in the mapper example for simplicity, but did in the library. Declaring the mapped functions as static,

static MatrixType dosomething(MatrixType a) {/* do something */}

will help you maintain you code latter since static functions don't have their symbols exported the way non-static functions do. This has no effect on programs, but in a library it enables you to modify your code without breaking binary compatibility. Just a tip...


Back Table of content Next

Nicolas Brodu, 2000-06-17