DIGITAL logo   C++ Graphic
    Updated: 21 April 1999
  Compaq C++

Compaq C++
Using Compaq C++ for OpenVMS Alpha


Previous Contents Index


Chapter 6
Handling C++ Exceptions

Exception handling is a C++ language mechanism for handling unusual program events (not just errors). On OpenVMS systems, Compaq C++ implements the exception handling model described in the C++ International Standard.

This includes support for throwing and catching exceptions, and calling the terminate() and unexpected() functions. C++ exception-handling support is implemented using functions and related OpenVMS system services that comprise the OpenVMS condition-handling facility. Hence, C++ exception-handling support is fully integrated with existing uses of the OpenVMS condition handling facility.

6.1 Compiling with Exceptions

Because exceptions are enabled by default, you need not specify the /exceptions qualifier whenever you compile the program.

For more information about the /exceptions qualifier see Section 6.3.

6.2 Linking with Exceptions

If any files in your program contain throw expressions, try blocks, or catch statements, or if any files in your program are compiled with the exceptions, you must link your program using the cxxlink facility (see Section 1.3 for more information on this facility). For example:


 $ cxxlink my_prog.obj 

Using the cxxlink facility ensures that the run-time support for exceptions (sys$library:libcxxstd.olb) is linked into your program.

Linking with /nosysshr (OpenVMS Version 6.2)

If you are running OpenVMS Version 6.2 or later, and you want to link using the /nosysshr qualifier, you must specify a linker options file on your cxxlink command. Otherwise, your link might fail because of undefined symbol references.

The linker options file should contain the following:


 sys$share:librtl.exe/shar 

For example, if cxx_exc.opt is your linker options file containing the above line, then a possible link command would be:


 $ cxxlink my_prog.obj, my_disk:[my_dir]cxx_exc.opt/opt 

Because the necessary run-time libraries are not provided in object format on OpenVMS Version 6.1 and earlier releases, linking with /nosysshr on those systems is not recommended.

For more information about linking with /nosysshr and about OpenVMS linker options files see the OpenVMS Linker Utility Manual

6.3 The /exceptions Qualifier

This section describes the /exceptions qualifier in detail.

/exceptions[=[no]cleanup] (Default)

/noexceptions

Controls whether support for C++ exceptions is enabled or disabled. C++ exceptions with the cleanup option are enabled by default.

When exceptions are enabled, the compiler generates code for throw expressions, try blocks, and catch statements. The compiler also generates special code for main programs so that the terminate() routine is called for unhandled exceptions.

You can control the cleanup of automatic objects during exception processing by using the [no]cleanup option:

  • When /exceptions=cleanup is in effect, the compiler generates cleanup code for automatic objects. As a result, when an exception is handled at run-time and control passes from a throw point to a handler, destructors are called for all automatic objects that were constructed since the try block containing the handler was entered.
  • When /exceptions=nocleanup is in effect, this cleanup code is not generated. You can reduce the size of your executable image by using /exceptions=nocleanup if you want to throw and handle exceptions, but cleanup of automatic objects during exception processing is not important for your application.

Specifying /noexceptions disables C++ exceptions as follows:

  • The compiler diagnoses warnings for throw expressions, try blocks, and catch statements, but may generate code for these constructs.
  • The compiler does not generate cleanup code for automatic objects.
  • The compiler does not generate special code for main programs so that the terminate() function is called for unhandled exceptions.

If any files in your program contain throw expressions, try blocks, or catch statements, or if exceptions are enabled, you must link your program using the CXXLINK facility. See Section 6.2.

6.4 The terminate() and unexpected() Functions

The unexpected() and set_unexpected() functions are implemented as defined in the IS.

By default, the unexpected() function calls the terminate() function.

The terminate() and set_terminate() functions are implemented as defined in the IS. The terminate() function is called if no matching handler is found for a thrown exception. By default, the terminate() function raises the OpenVMS condition cxxl$_terminate, and then calls the abort() function.

No stack unwinding is done by the terminate() function. Hence, no destructors are called for constructed objects when a thrown exception results in a call of the terminate() function. Instead, the program is terminated.

If a C++ function is called from a program in which the main function is not C++, terminate() is not called. Instead, the call stack points to the point of the throw.

The terminate() function is also called in the following additional cases:

  • An exception is thrown by a destructor or copy constructor called from the C++ exceptions handling support.
  • An internal error occurs in the C++ exceptions handling support.

6.5 C++ Exceptions and Other OpenVMS Conditions

Because C++ exceptions are implemented using the OpenVMS condition handling facility, C++ modules will work properly when they are part of a program that makes other uses of OpenVMS condition handling.

The raising and handling of an OpenVMS condition can result in the destruction of C++ automatic objects. If the handling of an OpenVMS condition results in an unwind through a C++ function's stack frame, then destructors will be called for automatic objects declared in that stack frame, just as if a C++ exception had been caught by a handler in an outer stack frame.

The C++ exception handling facility can also be used to catch OpenVMS conditions that are raised independently of C++ throw expressions. Except for those OpenVMS conditions that result in the delivery of signals, a C++ catch(...) handler will catch both C++ thrown exceptions and OpenVMS conditions. (For more information about OpenVMS conditions that result in the delivery of signals, see Section 6.6.)

You can use the data type struct chf$signal_array &, defined in the system header file chfdef.h, to catch OpenVMS conditions and to obtain information about the raised conditions. The C++ exceptions support transfers control to catch(struct chf$signal_array &) handlers when it determines that an OpenVMS condition was raised independently of a C++ throw statement.

If the catch (struct chf$signal_array &) handler specifies a class object, then the C++ exceptions support sets the class object to be a reference to the raised OpenVMS condition's signal argument vector. In the following example, obj.chf$l_sig_name will have the value 1022 when it is printed:


#include <chfdef.h> 
#include <iostream.hxx> 
#include <lib$routines.h> 
main () 
{ 
    try { 
        lib$signal (1022); 
    } catch (struct chf$signal_array &obj) { 
        cout << obj.chf$l_sig_name << endl; 
    } 
} 

A catch(struct chf$signal_array &) handler will also catch a thrown object that is explicitly declared to be of type struct chf$signal_array &. In this case, the value of the catch handler's object is determined by the originally thrown object, not the OpenVMS signal argument vector.

You can also use the data type struct chf$signal_array * to catch both OpenVMS conditions and objects explicitly declared to be of type struct chf$signal_array *. If a catch(struct chf$signal_array *) handler specifies an object, then that object becomes a pointer to the thrown object.

For more information about OpenVMS conditions, see the OpenVMS Calling Standard.

6.6 C++ Exceptions and Signals

Certain OpenVMS conditions (as described in the DEC C Run-Time Library Reference Manual for OpenVMS Systems) normally result in the delivery of signals. These signals can be processed using the signal handler mechanism described in the DEC C Run-Time Library Reference Manual for OpenVMS Systems.

You can call the following run-time function to cause these OpenVMS conditions to be treated as exceptions, instead of signals:


cxxl$set_condition(condition_behavior signal_or_exc) 

This can be done by putting the following call in your program:


#include <cxx_exception.h> 
...
cxxl$set_condition (cxx_exception); 

After your program calls the cxxl$set_condition (cxx_exception) function you can then catch these exceptions using any of the following handlers:

  • catch(struct chf$signal_array &)
  • catch(struct chf$signal_array *)
  • catch(...)

To revert back to the default signal behavior, you can make the following call:


cxxl$set_condition (unix_signal); 

Caution

If you use the DEC C Run-Time signal mechanism, avoid doing a C++ throw from a signal handler because this action could terminate your program.

The following are defined in the header file cxx_exception.h:

  • The cxxl$set_condition() function
  • The condition_behavior {unix_signal=0, cxx_exception=1 } enumeration type

The cxxl$set_condition function returns the previous setting. This function affects all threads in a process.

6.7 C++ Exceptions with setjmp and longjmp

Use the setjmp() and longjmp() routines with C++ exceptions carefully. In most cases, calling longjmp() in a C++ program will result in the proper destruction of automatic objects and the proper transfer of control. However, destructors may not be called for objects declared in a function when that function is the target of longjmp(). For example, in the following case, the destructor for object Obj_ok gets called when the setjmper() function returns, but the destructor for object Obj_never never gets called because the scope of object Obj_never is never properly exited:


setjmper () 
{ 
    some_type Obj_ok; 
 
    if (setjmp() == 0) { 
        some_type Obj_never; 
        ...
        call_longjmper();     // does a longjmp. 
    } 
        ...
} 

In the following example, Obj_twice gets constructed twice, but its destructor is called only once:


setjmper () 
{ 
     first_time = 1; 
     if (setjmp() == 0) { 
      ... 
     } 
     some_type Obj_twice; 
     ...
     if (first_time) { 
        first_time = 0; 
        call_longjmper();    // does a longjmp. 
     } 
} 

On OpenVMS VAX systems, if a function calls setjmp(), then C++ exceptions support is disabled for that function. This means the following:

  • No exceptions can be caught by the function's catch handlers.
  • No destructors will be called for the function's automatic data if an exception propagates through the function.
  • The unexpected() function will not be called for that function.
  • If setjmp() is called from main(), then terminate() will not be called for an unhandled exception.

On OpenVMS Alpha systems, if a function calls setjmp(), then C++ exceptions support is enabled for that function.

On OpenVMS VAX systems, the execution of destructors may cause your program to hang when the destructors are invoked as a result of a call to longjmp().

On OpenVMS VAX systems, longjmp() disables ASTs when unwinding the stack. If any destructors that are executed as a result of a call to longjmp() depend on the delivery of ASTs, then your program will hang because the ASTs will not get delivered. Destructors that do not depend on the delivery of ASTs are not affected by this.

Note that the cxxl$set_condition() function depends on the delivery of ASTs, and hence should not be called from destructors that get invoked during the execution of a call to longjmp().

This problem does not exist on OpenVMS Alpha systems because a call to longjmp() on OpenVMS Alpha does not disable ASTs.

6.8 C++ Exceptions, lib$establish and vaxc$establish

If a C++ function calls either the lib$establish() or the vaxc$establish() routine, then C++ exceptions support is disabled for that function. This means the following:

  • No exceptions can be caught by the function's catch handlers.
  • No destructors will be called for the function's automatic data if an exception propagates through the function.
  • The unexpected() function will not be called for that function.
  • If either lib$establish() or vaxc$establish() is called from main(), then terminate() will not be called for an unhandled exception.

6.9 Performance Considerations

The compiler optimizes the implementation of exception handling for normal execution, as follows:

  • Applications that do not use C++ exceptions and are compiled with the /noexceptions qualifier incur no run-time or image size overhead.
  • Applications compiled with exceptions enabled that have try blocks or automatic objects with destructors incur an increase in image size.
  • As much as possible, the run-time overhead for exceptions is incurred when throwing and catching exceptions, not when entering and exiting try blocks normally.

6.10 C++ Exceptions and Threads

C++ exceptions are thread safe. This means that multiple threads within a process can throw and catch exceptions concurrently. However, exceptions do not propagate from one thread to another, nor can one thread catch an exception thrown by another thread.

The set_terminate() and set_unexpected() functions set the terminate() and unexpected() handlers for the calling thread. Therefore, each thread in a program has its own terminate() and unexpected() handlers.

If you want every thread in your program to use the same nondefault terminate() or unexpected() handlers, then you must call the set_terminate() and set_unexpected() functions separately from each thread.

For more information about threads, see the Guide to DECthreads manual.

6.11 Debugging with C++ Exceptions

You can use the OpenVMS Debugger set break/exception command to set a breakpoint when an exception is thrown. You can use the show calls command to determine the location where the exception was thrown.


Previous Next Contents Index

   
Burgundy bar
DIGITAL Home Feedback Search Sitemap Subscribe Help
Legal