Exceptions

When a low level function encounters an error (like a new statement failing), it normally returns that error to it's caller. It does this because it doesn't know what action should be taken for the situation. The caller of the function can identify the error by the return value, and then decides what to do, possibly returning another error to a caller, or handling the error there.

This low-level encounter of an error, and the 'trickle-up' of the error code until it finally reaches the error handler can be a tedious thing to manage and enforce. C++ introduces the idea of exceptions to help deal with this management.

 

Exception Keywords

An exception is usually, but not always, an error. When a piece of code encounters an error, it may throw an exception. Some where, an exception handler is setup to catch the exception. Program flow goes directly from the code that throws the exception, to the code that catches it. Only destructors for objects successfully constructed are executed between the time of the throw, and the catch.

A throw normally only occurs inside a try block. You will find try blocks and catch blocks together, in a function. Note that the throw for a try block may be in the try block, or in any function called by the try block directly or in-directly. A throw that occurs outside a try block, will normally cause program termination, with an error message.

 

The try and catch blocks and throw

A try and catch block is indicated in the following manner:

try
{
    cout << "This is a test" << endl;
    throw 5;
    cout << "This will never be executed" << endl;
}
catch ( int MyErr ) // Handles int type exceptions
{
    cout << "I caught an exception of " << MyErr << endl;
}
catch( ... ) // Handle all exceptions (except ints, which are handle above
{
    cout << "I don't know what happened! << endl;
}

The try block in this example will throw an exception 5, an integer. The catch has been set up to handler (or catch) integer-type exceptions because the type of variable inside it's parens is an integer. A catch block receives a certain type of exception, and you may have several catch blocks (for different data-types of exceptions), or one catch block to handle all exceptions (which is declared with ... in it's parens). You can also throw a class as an exception. try and catch blocks may be nested. Note that the order of the catch statements is important.

 

Re-throwing an exception

Inside the catch block, you may throw a new exception if you like, or re-throw the previous exception by simply using throw;, with no operand.

 

Catching All Exceptions

As demonstrated above, a try block may have several catch blocks. Each block is tested against the specific exception type, until a matching type is found. If no match is found (either exact or via base/derived class matching), then the exception is raise again, automatically, to the next nested try/catch block.

In order to safe guard your code from unexpected exceptions, you can use the 'catch-all' catch block, which is a catch block with the ellipses (…) in it. This is demonstrated in the demo program above.

 

Exception Classes

Most class libraries like MFC and OWL, provide you with an exception base class and several classes derived from it. In Java, the exception classes are part of the language definition and are also organized as a base exception class, and derived classes.

The purpose of this is to simplify the exception handling and reporting, using the polymorphic attributes of the language (that’s virtual functions to C++ programmers). For example, imagine the following base exception (abstract) class:

class Exception
{
public:
Exception() { Msg[0] = '\0';}
const char *GetMsg() const { return( Msg ); }
virtual ~Exception() {};
virtual const char * GetType() const =0;
protected:
void SetData( char *nMsg ) { strcpy( Msg, nMsg ); }
char Msg[128];
};
ostream& operator<<( ostream& o, const Exception* E ) // Note: E is a pointer to a base class
{
o << "Exception of type " << E->GetType() << "\n" << E->GetMsg() << endl;
return( o );
}

 

This base class provides the framework for storing and outputting exception information. It’s an abstract class, so we will never instantiate an Exception class. Part of the ‘framework’ that this Exception base class provides is:

Since this base class handles the storing, and output of the descriptive string, the derived exception classes become very easy to write. Note the two possible examples below:

// Memory exception class, derived from Exception
class MemoryException : public Exception
{
public:
  MemoryException( char * Text ) { SetData( Text ); }
  virtual const char * GetType() const { return("MemoryException"); }
};
// File exception class, derived from Exception
class FileException : public Exception
{
public:
  FileException( char * Text ) { SetData( Text ); }
  virtual const char * GetType() const { return("FileException"); }
};

Note, in the real world, each different exception type would retain information specific to what caused the exception. For example, the FileException might also contain the name of the file that caused the exception.

 

Standard Exception Classes

The STL implements two standard exception classes. Most professional object libraries will implement their own set of exception classes, and may or may not be derived from the exception base class.

exception specification

A function that throws an exception, can specify the exception types after the function declaration. For example:

void Foo() throw( Type1, Type2 );

Is a function which can throw a Type 1 or Type2 class exception, or some type derived from Type1 or Type2. This specification is not a syntactical guarantee, but an assumption. If during the functions execution something not related to Type1 or Type2 is thrown, then the std::unexpected() function is invoked, which throws a bad_exception exception type.

exception

The exception class, defined in the <exception> header, defines the classes and data types common to exception handling, including exception handler function addresses.

The classes defined are:

class exception {

public:

exception() throw();

exception(const exception& rhs) throw();

exception& operator=(const exception& rhs) throw();

virtual ~exception() throw();

virtual const char *what() const throw(); // Provides message about the exception

};

class bad_exception : public exception {

};

As stated above, bad_exception is used when a function does not follow it’s exception specification.

The following functions are also defined:

fn set_terminate( fn ); Sets the terminate handler. Previous handler is returned.

fn set_unexpected( fn ); Sets the unexpected handler. Previous handler is returned.

fn is a pointer to a function with the following format: void (*fn)()

void terminate() Calls the terminate handler when an unhandled exception occurs

bool uncaught_exception() Returns true if an exception is uncaught.

void unexpected() Calls the unexpected exception handler when a function throws an unspecified

In the <stdexception> class, you will also find the following classes derived from the exception class:

class logic_error;
class domain_error;
class invalid_argument;
class length_error;
class out_of_range;
class runtime_error;
class range_error;
class overflow_error;
class underflow_error;

 

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

class Exception { // Our abstract, base exception class
public:
    Exception() { Msg[0] = '\0';}
    const char *GetMsg() const { return( Msg ); }
    virtual ~Exception() {};
    virtual const char * GetType() const =0;
protected:
    void SetData( char *nMsg ) { strcpy( Msg, nMsg ); }
    char Msg[128];
};

ostream& operator<<( ostream& o, const Exception* E ) // Note: E is a pointer to a base class
{
    o << "Exception of type " << E->GetType() << "\n" << E->GetMsg() << endl;
    return( o );
}

// Memory exception class, derived from Exception
class MemoryException : public Exception
{
public:
    MemoryException( char * Text ) { SetData( Text ); }
    virtual const char * GetType() const { return("MemoryException"); }
};

// File exception class, derived from Exception
class FileException : public Exception
{
public:
    FileException( char * Text ) { SetData( Text ); }
    virtual const char * GetType() const { return("FileException"); }
};

int AskUser( char * Prompt )
{
    char Ch;
    cout << Prompt << endl;
    do {
        cin >> Ch;
        cin.ignore(1);
    } while( Ch!='n' && Ch!='N' && Ch!='y' && Ch!='Y' );
    return( Ch=='y' || Ch=='Y' );
}

void GetMemory()
{
    if( AskUser( "Simulate a memory error?" ) )
        throw new MemoryException( "GetMemoryFailed");
}

void GetFile()
{
    if( AskUser( "Simulate a file error?" ) )
        throw new FileException( "GetFileFailed");
}

void main()
{
    try {
        GetMemory();
        GetFile();
    }
    catch( Exception * E ) { // Note how 1 handler, reports all exceptions
        cout << E;
        delete E; // Clean up the exception
    }
}