dunk (10/28/82)
Someone has recently suggested the use of setjmp/longjmp in C programs as a substitute for exception handlers as present in modern languages like ADA, MESA, CLU, etc. Having never used setjmp/longjmp, I thought it might be fun to try them in an approximation of ADA exception handlers: 1) The main program prepares to "catch" all exceptions, i.e. provide a default error handler, by calling setjmp(env). The return value (zero in this case) is used as the selector of a switch statement; non-zero return values indicate that an exception has been caught. 2) Whenever a routine notices an error, it can "raise" an exception by calling longjmp(env, val), where val is the type of exception. Control will be passed to the most recent caller of setjmp; it will appear as if setjmp has returned the value val. The caller of setjmp then handles the error, automatically aborting any subfunctions. 3) Should a function wish to "catch" an error locally, it simply remembers the current value of env, and calls setjmp(env). If an error is caught, it can either be handled, or the error can be propagated back by restoring the old env, and calling longjmp(env, retval), where retval is the exception type returned by setjmp. Also, should this function return control normally, it must remember to un-catcth exceptions (i.e. restore the old env). This might work, but it certainly relies on a lot of incantations. In very large projects, where exception handling is an important issue, the programming staff is often unexperienced with "normal" programming practices, much less incomprehensible invocations of setjmp/longjmp. What good is exception handling if it creates exceptions as often as it handles them? Also, as sketched out above, it is impossible for an exception handler to decide to return to the point of exception (ADA doesn't allow this either, but MESA does). Furthermore, consider real-time behavior. A good implementation of exception handlers only incurs real-time when an exception is raised; setjmp/longjmp pays whenever arrangements are made to catch an exception. Also, it is not clear to me how setjmp/longjmp interacts with hardware/OS raised exceptions. It is also not clear to me how "real" exception handlers would fit into C. As a related point, what are peoples feeling about C's philosophy of little compile-time and run-time checking. Back in school, when I used PASCAL to write programs more complex than I write now with C, I found that my programs were 80% debugged by the time they compiled. The remaining 20% was usually trivial since I would usually receive immediate indication of the error (e.g. array index out of bounds, NIL or unassigned pointer dereference, etc.). With C, compilation is usually a snap (though it is easy to puzzle over a "syntax error" message), but debugging often leads to mysterious "bus error - core dumped" types of messages. Now, I understand that one of C's reasons for not doing much checking is to save real-time during program execution, which I suppose is justifiable. Unfortunately, in my experiences with those very large projects I alluded to earlier, I find that, for "system reliability", I must insert almost the same checks that a good compiler would (assuming this compiler could optimize out the redundant checks). Surely manual array bounds-checking in C programs is less efficient than a compiler that has access to nifty instructions. Maybe I'm wrong. Opinions? Tom Duncan Bell Labs ihuxf!dunk