Argument Checking Policies

By default, when accessing elements in a Grid, Schnek does not check if the index falls within the valid range of the grid. This behaviour is much like the standard behaviour of C and C++. If you request an element outside the range of the grid the code will still attempt to access some memory address. Whether that memory address falls within the memory allocated for the grid or not depends circumstances and you might end up with non-sensical data or, if you are lucky, a segmentation fault.

Grid<double, 3> grid(Array<int, 3>(3,3,3));

// anything could happen here
grid(-1,5,10) = 1.0;

The code snippet above shows an example of accessing a grid out of range. The result is undefined. You could end up crashing the code or you could unknowingly overwrite a memory address that belongs to some other variable. This behaviour is usually the best for production code that is known to be stable because no checking means that the code runs faster. On the other hand, during the process of developing a simulation code, you might wish for some notification when you are exceeding the valid index range. Schnek allows you to choose between different agrument checking policies by specifying the third template parameter of the Grid class. By default this template argument is GridNoArgCheck. The other checking policy that has been implemented in Schnek is the GridAssertCheck class. Let’s modify the code above with a different argument checking policy.

Grid<double, 3, GridAssertCheck> grid(Array<int, 3>(3,3,3));

// causes a runtime assertion failure
grid(-1,5,10) = 1.0;

Now the program is guaranteed to stop as long as C style assertions are enabled.

Similar to the Grid class, the Array class also has two different checking policies. Let’s look at an example.

Array<int, 3> a;

// undefined behaviour
a[5] = 1;

Again, this results in undefined behaviour and is not guaranteed to flag up as an error. However, we can change the third template parameter of the Array class like this.

Array<int, 3, ArrayAssertArgCheck> a;

// causes a runtime assertion failure
a[5] = 1;

I practice it is useful to use typedefs in a header file to enable global switching of the argument checking policies. The following code shows an example of how this might be done.

#ifdef DEBUG_SIMULATION
typedef GridAssertCheck GridCheck;
typedef ArrayAssertArgCheck ArrayCheck;
#else
typedef GridNoArgCheck GridCheck;
typedef ArrayNoArgCheck ArrayCheck;
#endif

typedef Grid<double, 3, GridCheck> SimGrid;
typedef Array<double, 3, ArrayCheck> SimArray;

Now, by defining the macro DEBUG_SIMULATION argument checking can be turned on globally.