Data Structures and Algorithms
Errors in C ADTs

What can we do in C?

No I/O

Don't put I/O statements like printf in low-level re-usable code. If the code is to be re-usable, you want it to function correctly in any environment: I/O functions are generally the least portable aspects of any program. Leave them to the "higher" levels of any program where you certainly know what the operating environment of the program is.

Use the function value to return an error

If you were going to specify a void function, specify it as returning an int or better an enum instead.

Often there will be a value of the function return which can be used for indicating an error condition. Constructors return pointers to blocks of memory allocated for the object: a null pointer indicates an error.

int f( ... ) {
    X a;
    a = ConsX( ... );
    if ( a != NULL ) {
        /* No error */
        ....
        return 1;
        }
    else
        {
        /* return an error value to the next level up */
        return 0;
        }
    }

No useable function value?

One possible solution is to add a pointer to an error code to each method:
/* vector.h */
typedef struct vector_t *vector;
typedef enum( NoError, SizeMisMatch, NoMemory, InvalidVector )
                                 vector_error;

double AddVector( vector a, vector b, int *error );

The implementation:
#include "vector.h"
double DotProduct( vector a, vector b, vector_error *error ) {
    if ( LengthVector(a) == LengthVector(b) )
        {
        ....
        }
    else
        {
  *error = SizeMisMatch;
  return 0.0;
        }
  }
This solution, while achieving the aim of robust code, would probably be considered too cumbersome. The additional argument also adds to the execution time.

Emulate the exception handlers

setjmp/longjmp
There is a setjmp/longjmp mechanism in C which permits jumping from an arbitrary point to a handler. While it would be possible to use these, they pose potential portability problems, which are best avoided.
signal catching
Unix programs can also set a catcher for a signal. This mechanism was designed to enable a Unix parent process to monitor its children, so is perhaps an overkill for handling something that a user can trivially fix - like keeping too many windows open and using up all available memory!


Back to the Table of Contents
© John Morris, 1998