The following rules ensure that exceptions are used in a modular way (so that independent software components can be written without requiring knowledge of each other):
A naming convention ensures that the names for exceptions that are declared EXTERN from different modules do not clash. The following convention is recommended:
<facility-prefix>_<error-name>_e
Example: pthread_cancel_e
The TRY macro should only guard statements for which the statements in the FINALLY or CATCH, or CATCH_ALL clauses are always valid to execute.
A common misuse of TRY is to put code in the try_block that should be placed before TRY. An example of this misuse is as follows:
TRY handle = open_file (file_name); /* Statements that may raise an exception here */ FINALLY close (handle); ENDTRY
The previous FINALLY code assumes that no exception is raised by open_file. Otherwise, the code would access an invalid identifier in the FINALLY part if open_file is modified to raise an exception. The previous example should be rewritten as follows:
handle = open_file (file_name); TRY /* Statements that may raise an exception here */ FINALLY close (handle); ENDTRY
The code that is an opening bracket belongs prior to TRY, and the code that is its matching closing bracket belongs in the FINALLY clause.
Write functions that propagate exceptions to their callers so that the function does not modify any persistent process state before raising the exception. A call to the matching close call is required only if the open operation is successful. (If an exception is raised, the caller cannot access the output parameters of the function because the compiler may not have copied temporary values back to their home locations from registers.)
If open_file raises an exception, the identifier will not have been written, so open must not require that close be called when open raises an exception. This property is also what allows the call to be moved to open_file prior to the TRY.
It is invalid to return or go-to or leave by some other means a TRY, CATCH, CATCH_ALL, or FINALLY block. Special code is generated by the ENDTRY macro and it must be executed.
Variables that are read or written by exception handling code must be declared with the ANSI C volatile attribute. Run your tests with the optimize compiler option to ensure that the compiler thoroughly tests your exception handling code.
Reraise any exception that you catch, unless your handler has performed the complete recovery action for the error. This rule permits an unhandled exception to propagate to some final default handler that knows how to recover fully.
A corollary of this rule is that CATCH_ALL handlers must reraise, since they may catch any exception, and usually cannot do recovery actions that are proper for every exception.
Following this convention is important so that you also do not absorb a cancel or thread-exit request. These are mapped into exceptions so that exception handling has the full power to handle all exceptional conditions, from access violations to thread-exit. (In some applications it is important to be able to catch these to preserve an external invariant, such as an on-disk database.)