3.1.1 Error Codes3.1 Exception Handling3.1 Exception Handling3.1.2 Exceptions

3.1.1 Error Codes

A method is called and encounters a problem during its execution. Rather than terminating the program, the method wants to inform the caller about this problem and thus to delegate the decision how to handle the error. One possibility is to let the method return a special value, an  error code, that describes whether the method has successfully completed its operations, or, if not, which error has occurred. For instance, we may define a set of error codes

   final int OKAY = 0;        // result was okay
   final int IOERROR = 1;     // input/output error
   final int ARITHERROR = 2;  // arithmetic error

If our original method call was

   m(...);

we now have to write

   int result = m(...);
   if (result != OKAY)
   { 
     ... // error handling 
   }
   else
   {
     ... // normal operation
   }

If we have three methods of this kind that were originally called in sequence as

   m(...);
   n(...);
   o(...);

we now have to write

   int result1 = m(...);
   if (result1 != OKAY)
   { 
     ... // error handling 
   }
   else
   {
     int result2 = n(...);
     if (result2 != OKAY)
     { 
       ... // error handling 
     }
     else
     {
       int result3 = o(...);
       if (result3 != OKAY)
       { 
         ... // error handling 
       }
       else
       {
         ... // normal operation
        }
     }
   }

The program gets a little bit simpler, if we can return after the error handling to the caller:

   int result1 = m(...);
   if (result1 != OKAY)
   { 
     ... // error handling 
     return;
   }
   int result2 = n(...);
   if (result2 != OKAY)
   { 
     ... // error handling 
     return;
   }
   int result3 = o(...);
   if (result3 != OKAY)
   { 
     ... // error handling 
     return;
   }

Nevertheless, the textual overhead of error checking is high.

Another problem arises, if the method that may trigger the error is actually a function such that the return value is already in use. In this case, the method may set a global result variable to denote the success of its operation:

  int error;
  int m(...) { ... }

  void main(...)
  {
    int r = m(...);
    if (error != OKAY)
    {
       ... // error handling
    }
    else
    {
       ... // normal operation
    }
    ...
  }

However, this is not a very good style.

If the domain of the function result includes some values that are not returned by the function in normal situations, we may use some of these values as error values, e.g. a negative int value if the normal result is a non-negative int value or null if the result is an object pointer.

  Object r = m(...);
  if (r == null)
  {
    ... // error handling
  }
  else
  {
    ... // normal operation, use r
  }

If this is not possible, the result domain has to be extended to a pair of normal value and error code. For instance, we may define a class

  class Result
  {
    int value;
    int error;
  }

such that we can call the function as follows:

  Result r = m(...);
  if (r.error != OKAY)
  {
    ... // error handling
  }
  else
  {
    int i = r.value; // normal value
    ...
  }

However, this requires a change in the interface of the function.

A major problem with all these solutions is that they tend to yield ugly code with a lot of error checks such that the flow of control for the normal case can be hardly seen. This is the problem that is solved by exceptions.


© Wolfgang Schreiner; February 3, 2005

3.1.1 Error Codes3.1 Exception Handling3.1 Exception Handling3.1.2 Exceptions