Error Handler Pascal
Volume Number: 15
Issue Number: 9
Column Tag: Programming Techniques
A Simple Error Handler in Pascal
by Jim Phillips
One of the big differences between code you write for yourself and code you write for
others is the quality of the runtime error handling. Your users will be much happier
if you handle runtime errors gracefully. Gracefully means not destroying their data
and preventing system crashes when errors occur that are nobody's fault. Your users
have no recourse when your program misbehaves. They cannot debug or fix the code as
you can. Simply put, part of being professional is handling runtime errors.
Unfortunately, writing error handling code is one of the more boring and tedious tasks
that application programmers do. It therefore pays to simplify the writing of error
handling code as much as possible by capturing repeated code in a separate module and
reusing it throughout your application.
This has two additional benefits. First, it gives you an opportunity to put your
application's mark on the error handling rather than defer to the system, compiler,
or, possibly, third-party libraries. Second, it eliminates the need for a separate
console window for debug messages during development. If you have gone to the trouble
to create an attractive interface for displaying errors to the end user, it's surely good
enough for you.
Typically, you may have to do four things to handle an error.
• Check for the error.
• Report the error to the end user.
• Clean up.
• Exit from the failed procedure or function.
The last two items may have to be repeated for each procedure or function in a chain of
nested calls.
This article describes a module to organize and simplify the writing of error handling
code for Macintosh applications. Since you have the source, you can easily adapt it to
your application.
The source is presented in Apple's version of Pascal. However, the module can be
implemented in C or C++. A version in C++ is available at <ftp://ftp.mactech.com>.
Goals for the Error Handler
This section describes five goals for the error handler module.
First, the work horse error handling procedures should be short and easy to use
consistent with performance and reliability. If these procedures are not easy to use,
then they probably won't be.
Second, it should be easy for a client to write the error handling code without
introducing programming errors. It is very annoying when a low frequency problem
occurs and the user gets the wrong error message, or worse, no error message. Also
the error handling code that executes after an error is detected should not itself cause
crashes or destroy the user's data. This would be adding insult to injury.
Third, execution of the shipping code should be efficient in the absence of detected
errors. However, it is not so important for the code that displays the error and cleans
up the mess to be efficient. It's much more important that this code is correct and that
it succeeds.
Fourth, the error handling module should be as complete as possible. We should have a
convenient way to handle non-fatal, recoverable errors as well as fatal programming
errors (bugs).
Memory mismanagement is a common type of error in programming languages without
garbage collection. For this reason, the error handler should not try to allocate
memory after an error is detected. This can be avoided by allocating and locking all
memory required by the error handler early in the startup process.
In summary, our error handler module should have the following characteristics:
• Implementation of error handling should be easy for the client.
• Using the error handler should not be error prone.
• Normal successful execution should be efficient.
• It should handle everything from non-fatal errors to programming
errors.
• Error reporting should be safe even when memory is low.
Some of these goals are conflicting, so compromise will be necessary.
Handling Programming Errors in the Shipping Code
The standard thinking on debugging is that there should be two versions of your
application code: the debug version and the shipping version. The debug version
typically uses assertions and specialized testing code controlled by compiler
directives. This extra debug code handles errors that would be fatal if they occurred in
the shipping version. Errors that are nobody's fault, such as running out of memory,
are handled gracefully whether they are fatal or nonfatal and this error handling is
normally part of the shipping code. When all the bugs are found, the assertions and
specialized testing code are removed for the shipping version. This also removes all
overhead associated with the debug code, leaving only the no-fault error handling code.
We have our cake and eat it too.
Or do we? As a developer, does anything bother you about this description? How about
the part where we find all the bugs? And what is the consequence of removing all of our