|Ada 95 Quality and Style Guide||Chapter 5|
5.8.1 Handling Versus Avoiding Exceptions
When it is easy and efficient to do so, avoid causing exceptions to be raised.
Provide handlers for exceptions that cannot be avoided.
Use exception handlers to enhance readability by separating fault handling from normal execution.
Do not use exceptions and exception handlers as goto statements.
Do not evaluate the value of an object (or a part of an object) that has become abnormal because of the failure of a language-defined check.
In many cases, it is possible to detect easily and efficiently that an operation you are about to perform would raise an exception. In such a case, it is a good idea to check rather than allowing the exception to be raised and handling it with an exception handler. For example, check each pointer for null when traversing a linked list of records connected by pointers. Also, test an integer for 0 before dividing by it, and call an interrogative function Stack_Is_Empty before invoking the pop procedure of a stack package. Such tests are appropriate when they can be performed easily and efficiently as a natural part of the algorithm being implemented.
However, error detection in advance is not always so simple. There are cases where such a test is too expensive or too unreliable. In such cases, it is better to attempt the operation within the scope of an exception handler so that the exception is handled if it is raised. For example, in the case of a linked list implementation of a list, it is very inefficient to call a function Entry_Exists before each call to the procedure Modify_Entry simply to avoid raising the exception Entry_Not_Found. It takes as much time to search the list to avoid the exception as it takes to search the list to perform the update. Similarly, it is much easier to attempt a division by a real number within the scope of an exception handler to handle numeric overflow than to test, in advance, whether the dividend is too large or the divisor too small for the quotient to be representable on the machine.
In concurrent situations, tests done in advance can also be unreliable. For example, if you want to modify an existing file on a multiuser system, it is safer to attempt to do so within the scope of an exception handler than to test in advance whether the file exists, whether it is protected, whether there is room in the file system for the file to be enlarged, etc. Even if you tested for all possible error conditions, there is no guarantee that nothing would change after the test and before the modification operation. You still need the exception handlers, so the advance testing serves no purpose.
Whenever such a case does not apply, normal and predictable events should be handled by the code without the abnormal transfer of control represented by an exception. When fault handling and only fault handling code is included in exception handlers, the separation makes the code easier to read. The reader can skip all the exception handlers and still understand the normal flow of control of the code. For this reason, exceptions should never be raised and handled within the same unit, as a form of a goto statement to exit from a loop, if, case, or block statement.
Evaluating an abnormal object results in erroneous execution (Ada Reference Manual 1995, §13.9.1). The failure of a language-defined check raises an exception. In the corresponding exception handler, you want to perform appropriate cleanup actions, including logging the error (see the discussion on exception occurrences in Guideline 5.8.2) and/or reraising the exception. Evaluating the object that put you into the exception handling code will lead to erroneous execution, where you do not know whether your exception handler has executed completely or correctly. See also Guideline 5.9.1, which discusses abnormal objects in the context of Ada.Unchecked_Conversion.
|< Previous Page||Search||Contents||Index||Next Page >|