Exception handling

Exceptions are any unusual events (not necessarily errors) that are detectable by hardware or software and that may require special handling.

An exception is raised when the event occurs, the special processing is exception handling, and the routine which carries this out is the exception handler.

There are a wide variety of exception types, from hardware-detectable errors such as divide-by-zero, to file handling exceptions such as end-of-file, to subscript-checking exceptions (e.g. out-of-bounds array accesses).

Different exception handling facilities are provided in different languages - where a particular ability is not directly provided by the language it is the user's responsibility to devise their own handling for the event in question.

For example, the compiler for a particular language might automatically insert checks for array bounds or divide-by-zero cases: this results in a simpler and more readable user program since the user source code does not need to explicitly include the checks and handlers.

For any programming language we use, there are a number of factors we need to know:

Exception handling in C++

Exception handling in C++ consists of try blocks which define the scope of exceptions, and a subsequent sequence of catch blocks which handle exceptions generated in the preceding try block. I.e.
try {
   ...
   // normal processing
   ...
   if (/* SOME ERROR CONDITION IS DETECTED */) {
      throw exception_class_type(actual parameters); 
      // this creates an exception object, gives it some parameter data,
      //    and throws the object out to be (hopefully) caught and
      //    processed by an exception handler somewhere
    }
    ...
    // otherwise normal processing continues
   ...
}

catch (formal parameter exception class type 1) 
// handler 1, processes exceptions which are of class type 1
//    or which are derived from this class
{
   // handle the exception

   if (/* further exception handling deemed necessary */)
      throw; // re-throw unhandled exceptions

}

catch (formal parameter exception class type 2) {
// handler 2, processes unhandled exceptions which are of
//     class type 2, or which are derived from this class
}
...
When an exception is generated (thrown) the try block it occurs in is immediately terminated, and the "catch" statements following the block are sequentially searched.

The first catch whose parameter types match the thrown exception, is used for exception handling.

If a block is unable to handle the exception it can throw it again, hopefully to be caught by a subsequent catch block.

In general:

An example of exception handling:

In this example (modified from R. Sebesta's text "Concepts of Programming Languages") try and catch are used in reading and handling a list of grades.

#include <iostream>

class endofdata {
   public:
      endofdata() { }
};

class badgrade {
   public:
      badgrade(int g) { grade = g; }
      int getinput() { return grade; }
   protected:
      int grade;
};

int main()
// here we read in a list of grades and count how many fall
//    into each range: 0-9, 10-19, etc.
// exception handling is used to detect 
//    the end of input and invalid grades (out of range)
{
   int new_grade; // records a newly read grade
   int index;     // index into the array of grade distribution
   int lower_limit; // lower bound on a grade subrange
   int upper_limit; // upper bound on a grade subrange
   int distribution[10] = // record distribution of grades
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

   short int endofdata = 0; // flag to record the end of input

   // the overall try block reads the grades
   try {
       while (1) { // do forever, using the exception generator
                   //    to break out of the infinite loop when
                   //    the end of input data is reached

           // throw an exception at the end of input data
           if (!cin >> new_grade) throw endofdata;

           // since we haven't hit the end of the data yet,
           //    process the next grade
           {
              // the inner try block checks for out-of-range grades
              try {
                  // throw an exception on an out-of-range grade
                  if ((new_grade < 0) || (new_grade > 100)) 
                     throw badgrade(new_grade);

                  // otherwise process the grade
                  index = new_grade / 10;
                  if (index == 10) index = 9;
                  distribution[index] += 1;
              }

              // the inner catch block prints an error message
              //     for out-of-range grades
              catch (badgrade e) {
                 cout << "Error: grade " << e.getgrade() << " is invalid" << endl;
              }
           }
       }
   }

   // the overall catch block handles the end of data,
   //     displaying the grade distribution
   catch(endofdata e) {
      cout << "Range     Frequency" << endl;
      for (int i = 0; i < 10; i++) {
          lower_limit = i * 10;
          if (i == 9) upper_limit = 100;
          else upper_limit = lower_limit + 9;
          cout << lower_limit << "-" << upper_limit << " ";
          cout << distribution[i] << endl;
      }
   }

}

When to use exceptions

While the C++ feature is there and exploitable, it does not come without overhead: there is significant resource cost during execution, as well as reduced code clarity (and hence lower maintainability).

Generally accepted guidelines for the use of exceptions (as opposed to "normal" data handling) include:

C++ Exception Libraries

There exist C++ libraries defining a base set of exceptions, in the example below we use the stdexcept library, which defines a heirarchy of exception classes.

The base class for the heirarchy is exception, so to ensure safety we nest the contents of our main routine in a try block, and follow that with a general catch for the base exception type.

This guarantees that any unhandled (non-global) exception will be caught as long as we only ever throw descendants of the base exception class.

#include <iostream>
#include <stdexcept>
using namespace std;

void broken()
{
   // lets just throw an exception
   throw runtime_error("this is a runtime error");
}

int main()
{
   try {
      // call a function which will generate, but not handle,
      //    a standard exception
      broken();
   }
   catch (std::exception& e) {
      // catches any unhandled standard exceptions
      //     (or exception derived from them)
      cout << "Caught an unhandled exception: " << e.what() << endl;
   }
}
For more information on this library try man exception.