Programming Guide:xppguide

Offensive and defensive error handling Foundation

There are two "philosophies" in planning for handling error conditions during program development: one assumes that everything might be wrong and the other assumes that everything is right. These strategies have significant consequences on how procedures, user-defined functions, and methods are written, especially those that can receive arguments. Passing arguments to routines is a critical area for error handling because it frequently leads to errors, especially when individual arguments must have a specific data type or must lie within a specific range of values. A simple example is a user-defined function which performs a simple division operation.

FUNCTION Divide(nDividend, nDivisor) 
RETURN  nDividend / nDivisor 

This function presumes that both parameters are always of the numeric data type and that the parameter nDivisor is never equal to zero. If the programmer can guarantee these conditions, the function is acceptable as it is shown here. However, one must normally assume that incorrect arguments that could lead to a program error may be passed. Such a case can be avoided if the parameters are tested.

FUNCTION Divide(nDividend, nDivisor) 
   LOCAL nResult := 0 

   IF Valtype(nDividend) + Valtype(nDivisor)=="NN" .AND. ; 
      nDivisor <> 0 

      nResult := nDividend / nDivisor 

   ENDIF 
RETURN nResult 

In this example, the function covers all possible error conditions. It tests whether both parameters are of numeric data type and avoids division by zero. In this form, the function Divide() follows the defensive strategy of error handling because the function avoids all potential sources of error. If an incorrect parameter is passed, the function simply substitutes a numeric result (the value zero).

The strategy of avoiding the source of all possible errors guarantees stable programs, but can lead to a significant reduction in the runtime performance of programs. In a carefully programmed application errors do not occur often. The frequent testing for potential errors is not generally required and these tests use significant time.

Thus, the defensive strategy of error handling has a negative effect on the runtime performance of a program. This is because tests are frequently executed that are seldom necessary and are often superfluous. It is more advantageous to use an offensive error handling strategy which avoids superfluous test routines and assumes that no error will occur. This philosophy is basic in a 32bit operating system and should also be followed when programming using Xbase++.

The control structure BEGIN SEQUENCE...ENDSEQUENCE, in connection with the function ErrorBlock() and an error handling code block, form the basis for offensive error handling. The example function Divide() appears as follows using an offensive strategy:

FUNCTION Divide(nDividend, nDivisor) 
   LOCAL nResult, bError 

   bError := ErrorBlock( {|e| Break(e)} ) // install new error 
                                          // handling code block 
   BEGIN SEQUENCE 
      nResult := nDividend / nDivisor     // normal program code 
   RECOVER 
      ErrorBlock( bError )                // re-install old error 
                                          // handling code block for 
                                          // error handling 
      nResult := 0 
   ENDSEQUENCE 

   ErrorBlock( bError )                   // re-install old error 
                                          // handling code block 
RETURN nResult 

In this example, no test is performed on the passed parameters. Instead, it is assumed that the passed parameters always have the correct values and data types. The function contains two kinds of program code embedded in the BEGIN SEQUENCE ...ENDSEQUENCE structure. There is the program code which processes the normal, error free condition and additional program code that is executed if an error occurs. When an error occurs at runtime, the program code after the RECOVER statement is executed. Otherwise, this part of the function is skipped. In order for the RECOVER code to be executed, a local error handling code block is installed using the function ErrorBlock(). This code block calls the function Break() and is executed only if an error occurs. The function Break() interrupts the normal program execution and causes the program flow to continue after the RECOVER statement.

Although the function Divide() is not really needed, it demonstrates the two basic "philosophies" of runtime error handling. Offensive error treatment is generally advantageous since it provides improved runtime performance. For the programmer, offensive error handling means that, from inception of the development process, program code is organized into two parts. These parts are embedded in a BEGIN SEQUENCE...ENDSEQUENCE structure. One part contains the normal program logic. The other part captures error conditions and returns the program to a stable condition assuring a continued, uninterrupted run. Offensive error handling protects the main program code, which greatly improves the quality and maintainability of the program. All errors can be trapped with offensive error handling, which is not possible using the defensive strategy unless all possible conditions are explicitly tested. It should be noted that there is no universal rule to use in determining what program conditions are defined as errors and when simple error handling should be performed. The simple error conditions, like division by zero, are the exception rather than the rule. When in doubt, a subjective decision must be made concerning what constitutes an error and what form of error handling will be used. In many error conditions, it makes sense not to attempt error recovery, but to simply abort the program. In most error conditions, program termination is the default reaction of the error handling system of Xbase++ (see the file ERRORSYS.PRG).

Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.