Get Teem at Fast, secure and Free Open Source software downloads
teem / biff


The vast majority of teem functions calls that do anything important or substantial have an int return type. This is a Unix-system-call-style error code: it is zero if all is well, and non-zero if there was a problem. Because of laziness and simplicity, I have not developed conventions for what different error codes mean, so teem functions generally return 1 if something went wrong. Functions record details about exactly what went wrong by storing a text error message in biff.

Every error message to biff is keyed with a small string identifying what library or program is generating the error. In teem, that string is usually just the name of the library ("nrrd", "gage", etc.). In fact, the main header file for every teem library xxx has a #define XXX which expands to "xxx". This is intended to be used as the biff key for any errors in that library. A function at the top of a deep call-stack will record information about what the error condition was, and every other function down the stack can record contextual information.

For example, if a call to nrrdLoad() failed (with return value 1) because the header file refers to a non-existant data file, then the messages in biff tell the sad story:

test/tread: couldn't open nrrd "engine-crop.nhdr":
[nrrd] nrrdLoad:
[nrrd] nrrdRead: trouble reading NRRD
[nrrd] _nrrdReadNrrd: trouble parsing data file info "./engine-crop.raw"
[nrrd] _nrrdReadNrrdParse_data_file: fopen("./engine-crop.raw","rb") failed: No such file or directory
The first line isn't actually from biff, its from the stand-alone program which called nrrdLoad(). The last line actually quotes the fopen() error string, strerror(errno). The code which printed out this error information is (copied from in teem/src/nrrd/test/tread.c):
  if (nrrdLoad(nin=nrrdNew(), ninName)) {
    fprintf(stderr, "%s: couldn't open nrrd \"%s\":\n%s", me, ninName,
        err = biffGetDone(NRRD));
    free(err); exit(1);
Error messages can be transfered from one key to another, as in this case, where a nrrd error propagated to limn:
test/tcamanim: trouble making camera path:
[limn] limnCameraPathMake: trouble creating time warp spline
[limn] limnSplineCleverNew: couldn't create spline
[limn] limnSplineNew: given nrrd has problems
[limn] [nrrd] nrrdCheck: nrrd has NULL data pointer

Text-based error messages like this have benefits and drawbacks. The drawbacks is that until there is a uniform system of error codes to supplement and/or replace the text error messages, there is absolutely no way that a function can do any kind of error recovery. This weakness is unfortunately inhereted by all the teem libraries that use biff. For writing research code (I'm trying to get a PhD here), this hasn't been a problem. Any error is effectively a fatal error, and I want to know exactly what went wrong, rather than have my code get clever and try to "recover" from invalid input parameters. But the inability to systematically interpret errors may be more of an issue for other people wishing to use teem libraries in a non-research context. Assuming that the teem libraries reach some level of maturity in the coming years, robust error handling will become more of a priority. It hasn't been one so far.

The extremely compelling benefit of using text error messages is that you can immediately tell exactly what has gone wrong. In research contexts, where the priority is implementing novel functionality with experimental software, the last thing to waste time on is trying to interpret your own error codes, or deal with the consequences of not having specific error codes. Having a simple API for adding descriptive text error messages encourages individual tests for the various assumptions taken in a function, so the resulting biff message can be extremely specific. Half the lines of some nrrd functions, for example, are biff-based tests and error messages.