United States    
COMPAQ STORE | PRODUCTS | SERVICES | SUPPORT
| CONTACT US | SEARCH
C++
Compaq C++

Compaq C++
Using Compaq C++ for Tru64 UNIX Systems


Previous Contents Index


Chapter 7
Handling Exceptions

C++ incorporates an exception mechanism for handling unusual program events (not necessarily just errors). Exception handling enables you to detect program events and to provide handlers to deal with the events.

Compaq C++ implements the C++ exception handling model described in Chapter 15 of The Annotated C++ Reference Manual (which is repeated in §r.15 of The C++ Programming Language, 2nd Edition).

This chapter provides a brief introduction to exception handling, describes the run-time considerations of exception handling in Compaq C++, and recommends ways to use exception handlers for optimum performance. For a detailed description of C++ exception handling in general, see Chapter 9 and §r.15 in The C++ Programming Language, 2nd Edition.

Exception handling in Compaq C++ adds no significant overhead to applications that do not use the feature. No performance cost is associated with entering or leaving a try block.

Readers of this chapter should be familiar with the C++ exception-handling terminology, such as throwing and catching exceptions.

For information on debugging programs with exception handlers, see the Tru64 UNIX Ladebug Debugger Manual, which is included with the operating system documentation.

7.1 Structure

The following example shows the basic structure for declaring and using exception handlers in C++:


    .
    .
    .
void might_break(int i) 
{ 
    if (i == 1) throw "something!"; 
    if (i == 4) throw 4; 
    // ...
} 
 
void p (int i) 
{                                //        
    try                          //        begin try block 
    {                            //        
        might_break(i);          //        
    }                            //        
    catch (const char *p)        //        begin handler 
    {                            //         . 
        cout << "caught " << p;  //         . 
        // fix whatever...       //         . 
    }                            //        end try block 
}                                //        end handler 
    .
    .
    .

In this example, calling p with a value of anything other than 1 or 4 causes the program to execute normally, with no exception thrown. If p is called with a value of 1, the might_break function throws a string object to the p handler, which prints caught something! .

If p is called with 4, an int is thrown. Because p cannot catch values of type int , the search for a handler proceeds up the call stack until an appropriate handler can be found. If nothing on the stack can handle an int , program execution terminates immediately after calling the terminate function.

C++ exception handling represents a termination model, which means that program execution never proceeds past a throw . For additional information, see The Annotated C++ Reference Manual.

The following example shows a hierarchy of exceptions to detect and handle:


// program fragment to illustrate exception handling 
 
// Declare the possible exceptions: 
 
class exception {}; 
class failure : public exception {}; 
class process_exception : public exception {}; 
class system_not_running : public process_exception {}; 
class no_privilege : public process_exception {}; 
class no_such_system : public process_exception {}; 
// Add other process exceptions here...
 
// Remote_execute executes a command on a remote system 
void remote_execute ( 
          const char *command, 
          const char *system_name 
          ) throw (process_exception);    // an exception specification 
 
// The following function performs a remote execution, 
// waits indefinitely for a remote system to come up, and 
// prints error messages for other process__exception exceptions. 
 
#include <iostream.h> 
 
void protected_remote_execute ( 
          const char *command, 
          const char *system_name 
          ) throw (failure)          // an exception specification 
{ 
 
    try 
    { 
        for (;;) 
        { 
            try 
            { 
                remote_execute(command, system_name); 
                return; 
            } 
            catch (system_not_running) 
            { 
                // Insert delay code here. 
 
                continue; 
            } 
        } 
    } 
    catch (no_privilege) 
    { 
        cerr << "No privilege to execute process on remote system.\n"; 
    } 
    catch (no_such_system) 
    { 
        cerr << "Remote system does not exist.\n"; 
    } 
    catch (process_exception) 
    { 
        // Catch all process exceptions not dealt with above. 
 
        cerr << "Remote process execution failed.\n"; 
    } 
 
failure f; 
throw f; 
}          
 
    .
    .
    .

In this example, the inner try block detects and handles the system_not_running exception. The outer try block detects and handles the rest of the exceptions from the remote_execute function. When an exception occurs in the outer try block, the handlers are searched for a match in the order listed.

7.2 Run-Time Considerations

Compaq C++ optimizes the implementation of exception handling for normal execution, as follows:

In Compaq C++, a function with handlers has no intrinsic overhead. For example, functions with handlers do not have frame pointers or do not use additional registers.

Some functions without explicit handlers may have implicit handlers. The compiler creates a handler for each automatic object that has a destructor. The compiler also creates handlers for constructors that initialize subobjects that have destructors. In such a constructor, the compiler creates a handler for each member with a destructor, and a handler for each base class with a destructor. The -nocleanup option suppresses generation of such implicit handlers, which results in a slightly smaller executable file. Use the -nocleanup option for programs that do not use exception handling or do not require destruction of automatic objects during exception processing.

7.3 Coding Recommendations

Some recommendations for optimal results when using exception handling in Compaq C++ are:

7.4 Mixed-Language Applications

When C functions are intermixed with C++ functions, Compaq C++ treats them as C++ functions without exception handlers.

7.5 Hints on Using Exception Handlers

This section provides hints on using exception handling and elaborates on the coverage of some topics in The C++ Programming Language, 2nd Edition.

7.5.1 Propagating Changes from Exception Handlers

Changes to a caught object are not propagated if the object is caught as a value. For changes to propagate, the object must be caught as a reference. The following example shows how to propagate a change from a handler:


// In the first handler, changes made to the thrown object are not 
// propagated after the throw.  In the third handler, changes made 
// to the thrown object are propagated. 
// (Note that throw with no operand rethrows the current exception.) 
 
extern "C" printf(...); 
 
class A 
{ 
        int i; 
   public: 
        A(int ii = 0) : i(ii) { } 
        void display() { printf("%d\n",i); } 
        void reset() { i = 0; } 
}; 
 
int main () { 
 
    try 
    { 
        try 
        { 
            A a(3);             // create an object 
            throw a;            // throw it 
        } 
        catch (A aa)            // catch 
        { 
            aa.display();       // display current contents (3) 
            aa.reset();         // set contents to 0 
            aa.display();       // display contents after reset (0) 
            throw;              // rethrow -- will not propagate 
        } 
    } 
    catch(A ab) 
    { 
        ab.display();           // display contents -- (3) 
    } 
    try 
    { 
        try 
        { 
            A b(6);             // create an object 
            throw b;            // throw it 
        } 
        catch (A &ba)           // catch as a reference 
        { 
            ba.display();       // display current contents (6) 
            ba.reset();         // set contents to 0 
            ba.display();       // display contents after reset (0) 
            throw;              // rethrow -- will propagate 
        } 
    } 
    catch(A bb) 
    { 
        bb.display();           // display contents -- (0) 
    } 
 
return 0; 
} 

7.5.2 Using the unexpected and terminate Functions

If the unexpected function has an exception specification, Compaq C++ calls it when a function throws an exception that is not specified in the function's definition. You can specify your own unexpected function by calling the set_unexpected function.

On a normal return from an unexpected function, Compaq C++ passes control to the terminate function.

To allow your program to catch an unexpected exception, exit your unexpected function by throwing an exception. The following example shows how to return to a program from an unexpected function:


// This example shows how to return to the normal execution path by 
// exiting my_unexpected() with a throw. 
// 
// Output is: 
// 
//     In my_unexpected(). 
//     Caught HELP. 
// 
// Exit status is 0. 
 
#include "cxx_exception.h" 
 
extern "C" printf(...); 
extern "C" exit(int); 
 
typedef void(*PFV)(); 
PFV set_unexpected(PFV); 
 
void my_unexpected() 
{ 
    printf("In my_unexpected().\n"); 
    throw;                        // Rethrow the current exception. 
} 
 
void foo() throw ()               // Is not allowed to throw anything 
{ 
    throw "HELP";                 // Will result in a call to unexpected(); 
} 
 
int main () 
{ 
 
    // Set unexpected to be my_unexpected 
 
    PFV old_unexpected = set_unexpected(my_unexpected); 
 
    try 
    { 
        foo(); 
    } 
    catch(char * str) { printf("Caught %s.\n",str); } 
    catch(...) { printf("Caught something.\n"); } 
 
    set_unexpected (old_unexpected);           // restore unexpected 
    return 0; 
} 

On a normal return from an unexpected function, Compaq C++ passes control to the terminate function. Although changes made to the unexpected and terminate functions are per thread in higher versions of Compaq C++, take the precaution of restoring the unexpected and terminate functions to their previous values upon all returns from functions that change them using the set_unexpected or set_terminate functions.

7.5.3 Specification Conflicts

Problems can arise if conflicting exception specifications occur in function prototypes and definitions. This section describes how Compaq C++ handles these conflicts. You should avoid conflicting exception specifications, especially in prototypes made available to others in header files.

According to The Annotated C++ Reference Manual, an exception specification is not considered part of a function's type. Therefore, all of the following are legal constructs:

If no guidelines exist regarding such conflicts, Compaq C++ responds as follows:

The following example invokes a call to the unexpected function:


void foo() throw(int);              // prototype exception spec ignored 
void foo() throw() {throw 5;}       // throw 5 illegal after throw() 

The following example does not invoke a call to the unexpected function:


void foo() throw();                 // prototype exception spec ignored 
void foo() throw(int) { throw 5; }  // throw 5 legal after throw(int) 

7.5.4 Using the dlclose Routine

The dlclose routine cannot be used to delete a shared object ( .so ) until after any handlers handling an exception, thrown from the shared object, have exited. This restriction is necessary because, when exiting from a handler, the C++ exception support needs to reference data structures that were defined at the throw point.


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement