United States

Compaq C++

# Compaq C++
Using Compaq C++ for OpenVMS VAX

Previous | Contents | Index

# Chapter 6
Handling C++ Exceptions

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.

# 6.4 The unexpected() and set_unexpected() Functions

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:

- 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 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);

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

Compaq C++ 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 (the default) incur no run-time or image size overhead.
- Applications compiled with the `/exceptions` qualifier 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.

# 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.

- Exception specifications produce compilation errors if they are syntactically incorrect, regardless of whether they appear in a prototype or a definition.
- In determining the semantics of a function's exception specification, Compaq C++ uses only the specification in the function's definition. It ignores the prototype's exception specification.

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

privacy and legal statement