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...
Nicolas Brodu, 2000-06-17