AMMA Programming Notes
|
AMMA CONCEPTS
|
|
|
This page brings to your attention some important issues
that will tend to catch you out!
|
When using AMMA for the first time, you'll find there's a few surprises,
such as when dealing with BIG and SMALL objects.
This page will give details on:
The distinction between Big and Small objects
Coordinate systems in AMMA: the order of indices and vector components
Copying nested templated big objects
IndexC versus int?
The main difference is in the way the assignment operator and copy constructor
work. Small objects behave like basic 'C++' types, in which a new instance of
the object is created with every new variable and assignment make a copy of the
entire object. Big objects behave more like pointers: assignment only copies
the pointer to the body of the class. This allows you to have more than one
handle to a class. The object itself is stored in the heap. All the problems
of allocating and deallocating space on the heap are invisibly handled for you.
This may seem a bit confusing so here's an example:
int x,y; // Create two variables.
x = 2; // Assign the value 2 to x.
y = x; // Copy x to y.
y++; // Increment y.
As you would expect this code fragment leaves with x == 2 and y == 3.
Now let's use an integer array from AMMA:
Array1d<int> x(1),y(1); // Create a one element array.
x[0] = 2; // Assign value 2 to first element.
y = x; // x and y are now handles to the same object.
y[0]++; // This is now the same as x[0]++
This code leaves x[0] == 3 and y[0] == 3. If you wish assignment to make an
actual copy of the object you must do so explicitly:
Array1dC<int> x(1),y(1); // Create a one element array.
x[0] = 2; // Assign value 2 to first element.
y = x.Copy(); // y is now a copy of x.
y[0]++;
This behaves as the first example leaving with x == 2 and y == 3. Using the
copy constructor with big objects behaves in exactly the same was as the
assignment operator.
Array1dC<int> x(1); // Create a one element array.
Array1dC<int> y(x); // Create y with copy constructor.
x[0] = 2; // Assign value 2 to first element of x.
y[0]++; // x[0] is now equal to 3.
A typical example of big objects are VectorC, MatrixC, DListC and ImageC.
Some small objects are Vector2dC and Matrix2d2C.
If you are wondering when you should use a big object when writing a class yourself, here are some notes from Charles.
Copying nested templated big objects
What happens if I use the Copy() function to copy for example an
array of images? - like this:
Array1dC<ImageC<RealT> > a1(6), a2;
.... // initialise the images in a1
....
a2 = a1.Copy();
....
What exactly gets copied here? In fact only the array gets copied, not the
images themselves; i.e. you get a new array in a2 which contains
pointers to the original images in a1. If you want a "deep" copy,
so that all of the images in the array get copied into new images, you have to
do it explicitly:
a2 = a1.Copy();
for (IndexC i=a1.IMin(); i<=a1.IMax(); ++i) a2[i] = a1[i].Copy();
(If you have a large array, you can do the iteration more efficiently using an
array iterator, in this case Array1dIter2C.)
In 2D AMMA objects, such as arrays, matrices and images, the indices are in the
order: row, column (as in conventional matrix notation). In pairs of reals
(e.g. 2D vectors and points), the components are in the order x,
y. Normally these two categories of objects do not interact. However,
when they do (e.g. if we want to construct a pixel position from a 2D vector in
PixelC::PixelC(const
Point2dC & pt), the AMMA convention is this:
- The row index is associated with the x value
- The column index is associated with the y value
The implication is that AMMA adopts a 2D coordinate system in which the
x coordinate points downwards, and the y coordinate to the right.
Not what you were expecting? It has the advantages that:
- the coordinate system is anti-clockwise, as is conventional in mathematics
- the first array coordinate is associated with the first vector component etc.
The problems with the basic int type in C / C++ are manifold. Our
main complaints are:
- When an int is combined with an unsigned int,
the int is converted to unsigned int before the
operation is performed. This can lead to bizarre results for multiplication,
division and modulo operators where one of the operands is negative.
- The int division operator always rounds towards zero, as does
conversion from float or double, regardless of the
sign of the operands.
- The size of many AMMA objects is returned as unsigned, which
typically causes compilation warnings to be generated by comparison operations
in loops etc. unless IndexC is used.
We have attempted to address all of these problems in the IndexC
class (also typedefed as IndexT). So, for anything that could be
regarded as an index variable (for loop variables, array indices
etc.) we would recommend using IndexC.