Programming Guide:xppguide

Use of error objects Foundation

Objects containing information about runtime errors play a central role in the Xbase++ error handling system. Error objects are simple objects containing instance variables but no methods. An error object is automatically created when an error occurs and information about the error is contained in this object's instance variables. After an error object is created, it is passed to the error code block that was installed using the function ErrorBlock(). The error code block can call any functions or procedures and pass on to them the error object received by the code block. Using the function ErrorBlock(), user defined functions that use the information from an error object can be called to perform error handling when runtime errors occur.

In order to use the information in an error object, a parameter must be defined in the error code block. In case of an error, the parameter receives an error object that can be passed on to any function. The simplest case is passing the error object to the function Break() which continues the program flow after the RECOVER statement. This is implemented in the following program example. The program tests the operating readiness of drives. The test occurs in the user-defined function IsDriveReady() called from Main(). In the procedure Main(), a drive letter can be entered and this drive is tested for readiness.

PROCEDURE Main                         // test drives for 
   LOCAL cDrive := "C" , nReady        // readiness 

   CLS                                 // clear screen 
   DO WHILE LastKey() <> 27            // terminate with ESC key 

      @ 0,0 SAY "Drive to test:" GET cDrive // enter drive 
      nReady := IsDriveReady( cDrive ) // test drive 

      @ 10,10                          // position cursor and 
                                       // clear screen 
      IF nReady == 0                   // display message 
         @ 10,10 SAY  "Drive ready" 
      ELSEIF nReady == -1 
         @ 10,10 SAY  "Drive not ready" 
         @ 10,10 SAY  "Drive not found or invalid" 


#define  DRIVE_NOT_READY    21         // OS error code 

FUNCTION IsDriveReady( cDrive )        // Is drive ready ? 
   LOCAL nReturn   := 0 
   LOCAL cOldDrive := CurDrive()       // save current drive 
   LOCAL bError 
   LOCAL oError 

   bError    := ErrorBlock( {|e| Break(e) } ) 

      CurDrive( cDrive )               // change drive 
      CurDir( cDrive )                 // read current directory 

   RECOVER USING oError                // error has occurred 
      ErrorBlock( bError )             // reset error code block 
                                       // for error handling 

      IF oError:osCode == DRIVE_NOT_READY 
         nReturn := -1                 // drive not ready 
         nReturn := -2                 // drive not available 
      ENDIF                            // or invalid 


   ErrorBlock( bError )                // reset error code block 

   CurDrive( cOldDrive )               // and drive 

RETURN nReturn 

The user-defined function IsDriveReady() includes some important techniques for error handling. It starts by assigning the error code block {|e| Break(e) } which is executed if an error occurs. The code block passes an error object to the function Break(). For example, an error occurs when one of the characters "&/(123" is entered in Main() or when IsDriveReady("K") is called and drive "K" does not exist.

The function Break() continues the program flow after the RECOVER statement. In the example program, the RECOVER statement includes the USING option defining the variable oError. This variable receives the parameter passed to Break(), in this case it is the error object. The variable oError references an error object only after an error occurs. Otherwise it contains the value NIL and the program code between RECOVER and ENDSEQUENCE is not executed.

If an error occurs, an error object is automatically created and passed to the error code block. This error object contains information about the error in the values of its instance variables. Within the code block, the object is passed to the function Break() which branches to RECOVER. The error object is finally assigned to the variable oError specified in RECOVER USING. After an error occurs, the instance variables of the error object can then be accessed by the program code between RECOVER and ENDSEQUENCE. In the example, only the instance variable :osCode is inspected because it contains the error code for the errors that might occur during the test to determine the operating readiness of a drive (osCode = Operating System Code). Testing the drive is done by using the operating system which generates an error when a drive is not ready or does not exist. In the Xbase++ error handling system, the errors generated by the operating system are captured.

The function IsDriveReady() assigns the various return values in the error handling program code. This program code is not executed when the drive to be tested is ready. In this case, the essential program code contains only two lines. The function IsDriveReady() is a good example of the strategy of offensive error handling, demonstrates how errors can be captured, and shows how information about runtime errors can be used.

The most essential item for successful error handling is the error code block, which receives an error object. The error object contains information about the error that occurred. The program code for error handling appears between RECOVER and ENDSEQUENCE. The RECOVER statement occurs between BEGIN SEQUENCE and ENDSEQUENCE and can optionally receive a value as a parameter. The option RECOVER USING defines a variable to be assigned the value of the argument passed to Break().


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.