Previous | Contents | Index |
The bit_vector class provided in the library previous to the current compiler is no longer available. The current compiler provides this functionality with the ANSI C++ vector<bool> class. For example, consider a program making use of bit_vector as follows:
#include <vector> #include <iostream.h> int main () { bit_vector bv(3); bv[0] = bv[2] = 1; bv[1] = 0; bit_vector::iterator bi=bv.begin(); (*bi).flip(); cout << bv[0] << bv[1] << bv[2] << endl; return 0; } |
This would be coded as follows using the pre-ANSI <iostream.h> header:
#include <vector> #include <iostream.h> using namespace std; int main () { vector<bool> bv(3); bv[0] = bv[2] = true; bv[1] = false; vector<bool>::iterator bi=bv.begin(); (*bi).flip(); cout << (bool)bv[0] << endl; //cast vector<bool> subscript operator return 0; } |
Or it would be coded as follows using the ANSI <iostream> header:
#define __USE_STD_IOSTREAM #include <vector> #include <iostream> using namespace std; int main () { vector<bool> bv(3); bv[0] = bv[2] = true; bv[1] = false; vector<bool>::iterator bi=bv.begin(); (*bi).flip(); cout << bv[0] << bv[1] << bv[2] << endl; return 0; } |
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.
DIGITAL C++ implements the C++ exception handling model described in Chapter 15 of The Annotated C++ Reference Manual (which is repeated in The C++ Programming Language, 3rd Edition.
This chapter provides a brief introduction to exception handling, describes the run-time considerations of exception handling in DIGITAL 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 The C++ Programming Language, 3rd Edition.
Exception handling in DIGITAL 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
DIGITAL UNIX Ladebug Debugger Manual, which is included with the operating system
documentation.
8.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.
8.2 Run-Time Considerations
DIGITAL C++ optimizes the implementation of exception handling for normal execution, as follows:
In DIGITAL 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.
8.3 Coding Recommendations
Some recommendations for optimal results when using exception handling in DIGITAL C++ are:
. . . void foo(); volatile extern int sink; . . . void func1 (int z) { int y = 102; sink = y + y; try { int x = 88; foo(); sink = x + x; } catch (...){ { } } . . . |
When C functions are intermixed with C++ functions, DIGITAL C++
treats them as C++ functions without exception handlers.
8.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, 3rd Edition.
8.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; } |
If the unexpected function has an exception specification, DIGITAL 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, DIGITAL 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, DIGITAL C++ passes control to the
terminate
function. Although changes made to the
unexpected
and
terminate
functions are per thread in higher versions of DIGITAL 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.
8.5.3 Specification Conflicts
Problems can arise if conflicting exception specifications occur in function prototypes and definitions. This section describes how DIGITAL 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:
void foo(); void foo() throw(int) { // ... } |
void foo() throw(unsigned char); void foo() { // ... } |
void foo() throw(int); void foo() throw(float) { // ... } |
If no guidelines exist regarding such conflicts, DIGITAL 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) |
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 |