The unexpected( ) function
If your exception specification claims you’re going to throw a certain set of exceptions and then you throw something that isn’t in that set, what’s the penalty? The special function unexpected( )is called when you throw something other than what appears in the exception specification. Should this unfortunate situation occur, the default implementation of unexpectedcalls the terminate( )function mentioned earlier in this chapter .
The set_unexpected( ) function
Like terminate( ), the unexpected( )mechanism allows you to install your own function to respond to unexpected exceptions. You do so with a function called set_unexpected( ),which, like set_terminate( ), takes the address of a function with no arguments and voidreturn value. Also, because it returns the previous value of the unexpected( )pointer, you can save it and restore it later. To use set_unexpected( ), include the header file . Here’s an example that shows a simple use of the features discussed so far in this section: .
//: C01:Unexpected.cpp
// Exception specifications & unexpected()
//{-msc} Doesn’t terminate properly
#include
#include
#include
using namespace std;
class Up {};
class Fit {};
void g();
void f(int i) throw (Up, Fit) {
switch(i) {
case 1: throw Up();
case 2: throw Fit();
}
g();
}
// void g() {} // Version 1
void g() { throw 47; } // Version 2
void my_unexpected() {
cout << "unexpected exception thrown" << endl;
exit(0);
}
int main() {
set_unexpected(my_unexpected);
// (ignores return value)
for(int i = 1; i <=3; i++)
try {
f(i);
} catch(Up) {
cout << "Up caught" << endl;
} catch(Fit) {
cout << "Fit caught" << endl;
}
} ///:~
The classes Upand Fitare created solely to throw as exceptions. Often exception classes will be small, but they can certainly hold additional information so that the handlers can query for it .
The f( )function promises in its exception specification to throw only exceptions of type Upand Fit, and from looking at the function definition, this seems plausible. Version one of g( ), called by f( ), doesn’t throw any exceptions, so this is true. But if someone changes g( )so that it throws a different type of exception (like the second version in this example, which throws an int), the exception specification for f( )is violated .
The my_unexpected( )function has no arguments or return value, following the proper form for a custom unexpected( )function. It simply displays a message so that you can see that it was called, and then exits the program ( exit(0)is used here so that the book’s makeprocess is not aborted). Your new unexpected( )function should not have a returnstatement .
In main( ), the tryblock is within a forloop, so all the possibilities are exercised. In this way, you can achieve something like resumption. Nest the tryblock inside a for, while, do, or ifand cause any exceptions to attempt to repair the problem; then attempt the tryblock again .
Only the Upand Fitexceptions are caught because those are the only exceptions that the programmer of f( )said would be thrown. Version two of g( )causes my_unexpected( )to be called because f( )then throws an int .
In the call to set_unexpected( ), the return value is ignored, but it can also be saved in a pointer to function and be restored later, as we did in the set_terminate( )example earlier in this chapter .
A typical unexpectedhandler logs the error and terminates the program by calling exit( ). It can, however, throw another exception (or re-throw the same exception) or call abort( ). If it throws an exception of a type allowed by the function whose specification was originally violated, the search resumes at the call of the function with this exception specification. (This behavior is unique to unexpected( ).)
If the exception thrown from your unexpectedhandler is not allowed by the original function’s specification, one of the following occurs:
1. If std::bad_exception(defined in ) was in the function’s exception specification, the exception thrown from the unexpected handler is replaced with a std::bad_exceptionobject, and the search resumes from the function as before.
2. If the original function’s specification did not include std::bad_exception, terminate( )is called.
The following program illustrates this behavior .
//: C01:BadException.cpp
//{-bor}
#include // for std::bad_exception
#include
#include
using namespace std;
// Exception classes:
class A {};
class B {};
// terminate() handler
void my_thandler() {
cout << "terminate called\n";
exit(0);
}
// unexpected() handlers
void my_uhandler1() {
throw A();
}
void my_uhandler2() {
throw;
}
// If we embed this throw statement in f or g,
// the compiler detects the violation and reports
// an error, so we put it in its own function.
void t() {
throw B();
}
void f() throw(A) {
t();
}
void g() throw(A, bad_exception) {
t();
}
int main() {
set_terminate(my_thandler);
set_unexpected(my_uhandler1);
try {
f();
}
catch (A&) {
cout << "caught an A from f\n";
}
set_unexpected(my_uhandler2);
try {
g();
}
catch (bad_exception&) {
cout << "caught a bad_exception from g\n";
}
try {
f();
}
catch (...) {
cout << "This will never print\n";
}
} ///:~
The my_uhandler1( )handler throws an acceptable exception ( A), so execution resumes at the first catch, which succeeds. The my_uhandler2( )handler does not throw a valid exception ( B), but since gspecifies bad_exception, the Bexception is replaced by a bad_exceptionobject, and the second catch also succeeds. Since fdoes not include bad_exceptionin its specification, my_thandler( )is called as a terminate handler. Thus, the output from this program is as follows: .
caught an A from f
caught a bad_exception from g
terminate called
Better exception specifications?
You may feel that the existing exception specification rules aren’t very safe, and that
void f();
should mean that no exceptions are thrown from this function. If the programmer wants to throw any type of exception, you might think he or she should have to say .
void f() throw(...); // Not in C++
Читать дальше