United States |
Previous | Contents | Index |
Exception handling is a C++ language mechanism for handling unusual
program events (not just errors). For Compaq C++ on OpenVMS
systems, the exception-handling features are those defined in
The Annotated C++ Reference Manual, Chapter 15. 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
To use Compaq C++ exceptions, you must specify the /exceptions qualifier whenever you compile the program. For example:
$ cxx/exceptions my_file.cxx |
The default for this qualifier is /noexceptions . You must explicitly specify the /exceptions qualifier to ensure that the C++ compiler generates the correct machine code so that exceptions work properly in your program.
Note that specifying the /exceptions qualifier is important even when compiling modules that do not contain try blocks or throw statements. This is because the C++ compiler may need to generate machine code for exception-handling support in these modules. The purpose of this machine code is to ensure that destructors are called for automatic objects when an exception propagates through the blocks in which they are declared.
Failure to specify the /exceptions qualifier when compiling any C++ module may prevent the calling of destructors when an exception is thrown.
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 qualifier, then 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 wish to link using the /nosysshr qualifier, then you need to specify a linker options file on your cxxlink command. Otherwise, your link may 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]
/noexceptions (Default)
Controls whether support for C++ exceptions is enabled or disabled. If you want C++ exceptions enabled, you must specify /exceptions , which uses the cleanup option by default.You specify the /exceptions qualifier to enable support for C++ exception handling. When the /exceptions qualifier is in effect, 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 ( /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.
The default is /noexceptions , which 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 you want full exceptions support, you should compile all C++ files in your program 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 /exceptions , then you must link your program using the CXXLINK facility. See Section 6.2.
The unexpected() and set_unexpected() functions are implemented as defined in The Annotated C++ Reference Manual. Declarations for these functions can be found in the header file cxx_exception.h .
By default, the unexpected() function calls the terminate() function.
The terminate() and set_terminate() functions are implemented as defined in The Annotated C++ Reference Manual. 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.
The terminate() function is also called in the following additional cases:
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 Compaq C++ 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); |
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:
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:
Compaq C++ optimizes the implementation of exception handling for normal execution, as follows:
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.
6.12 Specification Conflicts
Try to avoid exception specifications where function prototypes are different from their definitions. However, if differences exist, Compaq C++ handles these conflicts as follows.
For example, the following code results in a call to the unexpected() function:
void foo() throw(int); // prototype exception spec ignored void foo() throw() { throw 5; } // throw of int is unexpected |
The following two cases do not result in a call to the unexpected() function:
void foo() throw(); // prototype exception spec ignored void foo() throw(int) {throw 5;} // throw of int is expected void foo() throw(char *); // prototype exception spec ignored void foo() {throw 5;} // no exception specification |
For more information about C++ exception specifications, see The Annotated C++ Reference Manual.
Previous | Next | Contents | Index |
|