exception

The need to handle error conditions is as old as programming itself, for, after all, are there programs that do not generate errors?

In the dim and distant past, before the Age of Exceptions, the accepted method was to return error code, usually an enumeration, from a function when an error was detected.

return_code do_something()


That was not enough in most circumstances, however, since: (a) not all the information required for debugging in case of an error was available within the method that detected the error, (b) there was no easy way of generating the call stack in case of an error. Thereby came the need for creating an error stack, where each method in the call stack added relevant information to a growing stack (most commonly implemented as a large pre-allocated string), and the 'main' method (or some method high up in the call stack that could handle the error), usually logged the error stack thus generated. Below is an example such error stack generation in C:

int main( int argc, char **argv ) {
   ...
   if (do_something(data) != SUCCESS) {
      log_error_stack( COMPONENT, FUNC, ERROR, "some more info" )
      return EXIT_FAILURE;
   }
   // do something else here
   return EXIT_SUCCESS;
}
 
return_code do_something( char *data ) {
   if (do_something_more(data) != SUCCESS) {
      add_to_error_stack( COMPONENT, FUNC, "some more info" );
      return FAILURE;
   }
   // do something else here
   return SUCCESS;
}
 
return_code do_something_more( char *data ) {
   if (is_bad_data(data)) {
      start_error_stack( COMPONENT, FUNC, "data is bad etc..." );
      return FAILURE;
   }
   // do something else here
   return SUCCESS;
}


This, of course, was a workable solution, except for the ugliness it created in the code itself. The code became interspersed with error handling statements, which were usually more voluminous than the actual code itself. Also, each method in the call stack needed to check the error returns, and add its name to the error stack in case of an error. There were other added complexities, such as in multi-threaded programming, where error stacks needed to be thread-local.

Then came the Age of Exceptions, and the language embraced error handling. You could throw an exception instead of returning an error code, and the stack trace would be created for you; you did not have to catch exceptions at every level of the call stack. Thread-locality of stack trace was maintained automatically. It also gave you the choice of handling exceptions or ignoring them at will. So, here is an example of exception handling in C++:

int main( int argc, char **argv ) {
   ...
   try {
      do_something(data);
   } catch (my_exception &e) {
      log( e.what() );
      return EXIT_FAILURE;
   } catch (...) {
      // do some cleanup
      return EXIT_FAILURE;
   }
   return EXIT_SUCCESS;
}
 
void do_something( char *data ) {
   do_something_more( data );
   // do something else here
}
 
void do_something_more( char *data ) {
   if (is_bad_data(data)) {
      throw my_exception( "data is bad etc..." );
   }
   // do something else here
}


That was life before Java. This was life before what is now known as checked exceptions.

Java brings in the choice of checked vs. unchecked exceptions. Wherever there is choice, there is controversy. So, when to use checked or unchecked exceptions?

But first of all, what exactly are checked and unchecked exceptions? A checked exception is derived from Java Exception class, and requires that you specify the exception type in the method signature when you throw such an exception within the method, and that any method that calls such a method in turn is required to explicitly handle such an exception. An unchecked exception, derived from Java RuntimeException class, has no such requirement; the method that throws such an exception need not specify it in the function signature; the calling method need not handle such an exception. By this definition, C++ exceptions are similar to unchecked exceptions.

Here is an example of checked exception in Java (we assume that MyBadDataException and MyTerribleDataException classes are derive from MyException, which, in turn, derives from Java Exception class):

public static void main(String [] args) {
   // initialize something
   try {
      doSomething(data);
   } catch (MyTerribleDataException e) {
      e.printStackTrace();
      ...
   } catch (MyBadDataException e) {
      e.printStackTrace();
      ...
   }
}
 
void doSomething( MyData data ) throws MyTerribleDataException, 
                                         MyBadDataException {
   doSomethingMore(data);
   // do something else here
   if (isTerribleData(data)) {
      throw MyTerribleDataException( "data is terrible etc..." );
   }
}
 
void doSomethingMore( MyData data ) throws MyBadDataException {
   if (isBadData(data)) {
      throw MyBadDataException( "data is bad etc..." );
   }
   // do something else here
}


So, when to use checked exceptions, and when not to?

The thumb rule is simple: if the calling method has the possibility of recovering from an exception, use checked exception; else use unchecked exception. For example, if your program can not find the all-important configuration file at startup, its likely that it will never be able to recover; that's a case for unchecked exception. On the other hand, if your program processes billions of documents, and you have an issue with processing one of them, the program can still continue with the rest of the documents; that's a case for checked exception.

So, what is the controversy about checked exceptions? To understand that lets go through some of the pros and cons of checked exceptions.

Pros:

1. Usage of checked exceptions leads to code discipline. Checked exceptions require complete method interface, including exceptions thrown, to be defined. It also requires that calling methods honor the interface defined by callee, and take suitable steps to handle the exceptions thrown. All non-conformances are detected at compile time.

Cons:

1. Proper use of checked exceptions leads to unwieldy interfaces. Methods that are part of the API must declare all the types of checked exceptions thrown. Of course, this is very often avoided (incorrectly) by having API-specific exception classes that all inherit from one base exception class, and then only declaring the base exception class at the API-level; this practice is at odds with the checked exception philosophy, since the interface, in this case, does not clearly state which exceptions are being thrown.

2. Proper use of checked exceptions leads to unwieldy code. For example, somewhere far up the call stack, a method will have to catch all the different types of checked exceptions that may be thrown by methods down the stack. This is frequently avoided (incorrectly) by: (a) catching only the base exception, (b) wrapping all the underlying exceptions into one application-specific exception. These practices, again, are at odds with the checked exception philosophy.

Here is an example of checked exception handling gone wrong (we assume that MyBadDataException and MyTerribleDataException classes are derive from MyException, which, in turn, derives from Java Exception class):

public static void main(String [] args) {
   // initialize something
   try {
      doSomething(data);
   } catch (MyException e) {
      e.printStackTrace();
      ...
   }
}
 
void doSomething( MyData data ) throws MyException {
   doSomethingMore(data);
   // do something else here
   if (isTerribleData(data)) {
      throw MyTerribleDataException( "data is terrible etc..." );
   }
}
 
void doSomethingMore( MyData data ) throws MyException {
   if (isBadData(data)) {
      throw MyBadDataException( "data is bad etc..." );
   }
   // do something else here
}


There is no denying that checked exceptions are better in general for code discipline, and should be used when the caller has the possibility of handling the exception thrown by callee. However, because of the unwieldy interfaces and code that it may lead to, at times the proper usage of checked exception is found to be compromised in real code. If this is the case in your code, its better to use unchecked exceptions.