7.7 Exception Handling Mechanisms in C

7.7 Exception Handling Mechanisms in C++

Ideally, the testing and debugging process will remove all defects from the software or at least as many defects as possible from the software. Unwanted and inconvenient conditions should be handled by regular program logic. After defects are removed and unwanted or inconvenient conditions are handled, everything left is an exception. Exception handling is supported in C++ by three keywords: try , throw , and catch . Any code that encounters an exceptional condition that it cannot cope with throws an exception hoping that some exception handler (somewhere) can handle the problem (Stroustrup, 1997). The throw keyword is used to throw an object of some type. Throwing the object transfers control to an exception handler coded to deal with the type of object thrown. The catch keyword is used to identify handlers designed to catch exception objects. For example:

 void importantOperation {    // executeImportCode()    // the Impossible Happens Somehow    impossible_condition ImpossibleCondition;    throw ImpossibleCondition;    //... } catch (impossible_condition &E) {    // Do something about E    //... } 

The importantOperation() routine attempts to do its work and encounters an unusual condition that it cannot cope with. In our example, it creates an object of type impossible_condition and uses the keyword throw to throw the object. The block of code that uses the catch keyword is designed to catch objects of type impossible_condition . This block of code is called an exception handler . Exception handlers are associated with blocks of code contained within a try expression. A try block is used to surround code that possibly contains a routine that will encounter some exceptional situation. A catch block may only follow a try block or another catch block. So we might have:

 try{    //...    importantOperation()    //... } catch(impossible_condition &E) {    // do something about E    //... } 

Here, either the routine importantOperation() or one of the routine importantOperation() calls have the potential to encounter some condition that it simply cannot cope with. The routine will throw an exception. Control will be transferred to the first exception handler that accepts an error of type impossible_condition . Either that routine will handle the exception or throw the exception to be handled by another exception handler. The objects thrown can be user -defined objects that can form simple to sophisticated error codes and messages. They may contain code that will help the exception handler perform its work. If we used objects like exception_response objects from Examples 7.1 and 7.2, they may be used by the exception handler to either correct the problem or allow the program to somehow recover its state. We may also use the built-in exception classes to create exception objects.

7.7.1 The Exception Classes

The standard C++ class library has nine exception classes divided into two basic groups. Table 7-3 shows the runtime error group and the logic error group. The runtime error group represents errors that are somewhat difficult to prevent. The logic error group represents errors that are "theoretically preventable."

Table 7-3. Runtime and Logic Error Classes

runtime error Classes

logic error Classes

range_error

domain_error

underflow_error

invalid_argument

overflow_error

length_error

 

out_of_range

7.7.1.1 The runtime_error Classes

Figure 7-4 shows the class relationship diagram for the runtime_error family of classes. The runtime_error family of classes is derived from the exception class. Three classes are derived from runtime_error: range_error , overflow_error , and underflow_error . The runtime_error classes report internal computation or arithmetic errors. The runtime_error classes get their primary functionality from the exception class ancestor . The what() method, assignment operator=() , and the constructors for the exception handling class provide the capability of the runtime_error classes. The runtime_error classes provide an exception framework and architectural blueprint to build upon.

Figure 7-4. The class relationship diagram for the runtime_error family of classes.

graphics/07fig04.gif

They offer little inherent functionality ”the programmer must specialize them through inheritance. For example, the defect_response and exception_response classes created in Examples 7.1 and 7.2 might be derived from either runtime_error or logic_error classes. Let's look at how the basic exception classes work with no specialization. Example 7.3 shows how an exception object and a logic_error object can be thrown.

Example 7.3 Throwing an exception object and a logic_error object.
 try{    exception X;    throw(X); } catch(const exception &X) {    cout << X.what() << endl; } try{    logic_error Logic("Logic Mistake");    throw(Logic); } catch(const exception &X) {    cout << X.what() << endl; } 

The basic exception classes have only construction, destruction, assignment, copy, and simple reporting capabilities. They do not contain the capability to correct a fault that has occurred. The error message returned by the what() method of the exception classes will be determined by the string passed to the constructor for the logic_error object. In Example 7.3, the string "Logic Mistake" passed to the constructor will be returned by the what() message in the catch block.

7.7.1.2 The logic_error Classes

The logic_error family of classes is derived from the exception class. In fact, most of the functionality of the logic_error family of classes is also inherited from the exception class. The exception class contains the what() method, used to report to the user a description for the error being thrown. Each class in the logic_error family contains a constructor used to tailor a message specific to that class. Figure 7-5 shows the class relationship diagram for the logic_error classes.

Figure 7-5. The class relationship diagram for the logic_error family of classes.

graphics/07fig05.gif

Like the runtime_error classes, these classes are really designed to be specialized. Unless the user adds some functionality to these classes, they cannot do anything other than report the error and the type. The nine generic exception classes provide no corrective action or error handling.

7.7.1.3 Deriving New Exception Classes

The exception classes can be used as-is, that is, they can be used simply to report an error message describing the error that has occurred. However, this is virtually useless as an exception handling technique. Simply knowing what the exception was doesn't do much to increase software reliability. The real value of the exception class hierarchy is the architectural road map that they provide for the designer and the developer. The exception classes provided basic error types that the developer can specialize. Many of the exceptions that occur in a runtime environment can be placed into either the logic_error or runtime_error family of classes. To demonstrate how to specialize an exception class, lets use the runtime_error class as an example. The runtime_error class is a descendant of the exception class. We can specialize the runtime_error class through inheritance. For instance:

 class file_access_exception : public runtime_error{ protected:    //...    int ErrorNumber;    string DetailedExplanation;    string FileName;    //... public:    virtual int takeCorrectiveAction(void)    string detailedExplanation(void);    //... }; 

Here, the file_access_exception inherits runtime_error and specializes it by adding a number of data members and member functions. Specifically, the takeCorrectiveAction() method is added. This method can be used to help the exception handler perform its recovery and correction work. This file_access_exception object knows how to identify deadlock and how to break deadlock. It also has specialized logic for dealing with viruses that can damage files as well as specialized knowledge for dealing with file transfers that get unexpectedly interrupted . Each of these situations can introduce runtime exceptions. We can use our file_access_exception objects with the throw, catch, and try facilities of C++. For instance:

 try{    //...    fileProcessingOperation();    //... } catch(file_access_exception &E) {    cerr << E.what() << endl;    cerr << E.detailedExplanation() << endl;    E.takeCorrectiveAction();    // Handler Take Additional Corrective Action    //... } 

This technique allows you to create ExceptionTable map objects similar to the ErrorTable map objects used in Examples 7.1. and 7.2 Using vertical and horizontal polymorphism will also simplify exception handler processing.

7.7.1.4 Protecting the Exception Classes from Exceptions

The exception objects are thrown when some software component encounters a software or hardware anomaly. But note, the exception objects themselves do not throw exception. This has many implications. If the processing of the exception is complex enough to potentially cause another exception to be generated, then the exception processing should be redesigned and simplified where possible. The exception handling mechanism is unnecessarily complicated when exception handling code can generate exceptions. Therefore, most of the methods in the exception classes contain the empty throw() specification.

 // Class declaration for exception class class exception { public:    exception() throw() {}    exception(const exception&) throw() {}    exception& operator=(const exception&) throw()       {return *this;}    virtual ~exception() throw() {}    virtual const char* what() const throw(); }; 

Note the throw() declarations with empty arguments. The empty argument shows that the method cannot throw an exception. If the method attempts to throw an exception, a compile-time error message is generated. If the base class cannot throw an exception, then the corresponding method in any derived class cannot throw an exception.



Parallel and Distributed Programming Using C++
Parallel and Distributed Programming Using C++
ISBN: 0131013769
EAN: 2147483647
Year: 2002
Pages: 133

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net