Run-Time Behaviour in C ++
9.6 try, throw and catch: INGREDIENTS OF THE C++ EXCEPTION MECHANISM
In order to understand exception handling we must discuss the concept of acontract between client code (the calling function) and the server code (the function being called). Each party has its rights and responsibilities. The server states what the conditions and rules are and the client should abide by these rules. If not, then there is no guarantee that the results are correct (or indeed that a result is returned at all). So, the client defines a block of code that captures any run-time errors, should they occur. The client calls a server function in this block. If the server discovers an error it will throw a newly created exception object back to the client.
In general, the steps to be taken by the developer are:
1. Client calls a server function from a clienttry block
2. Server checks if contract has been honoured (thepreconditions)
3. If contract has been honoured, the server’s postconditions are executed and control is returned to the client
4. If the contract has not been honoured, the server code throws an exception object back to the client which then catches it in itscatch block (there may be several such blocks) The main challenges for the developer are:
r
Determining what is and what is not an exceptionr
Where to placeprecondition code that checks for break of contract in the serverr
What data should we place in the exception object in order to help the client figure out what it did wrong?In general, we design exception classes so that they contain enough state to help the client code make decisions on what to do if an exception does occur at run-time. Essential information includes the following:
r
The type of exception thrown (e.g. ZeroDivide, OutOfBounds)r
The message text in the exception (this can be displayed on the client screen, for example)r
The source of the exception (in which server function did the exception occur?)It is important to realise that software should be as reliable as possible and steps should be taken to ensure that code recovers from abnormal situations.
9.7 C++ IMPLEMENTATION
We propose developing a C++ class hierarchy that models exceptions pertaining to vectors and numerical problems. To this end, we design a simple hierarchy as shown in Figure 9.1.
The design rationale is to design each class with enough information to help clients determine what the cause of the error is. In particular, we can support the features:
r
A message namer
The method that threw the exceptionFurthermore, derived classes can give extra information, for example the offending index for an OutOfBounds exception. We concentrate on ZeroDivide exceptions in this section and it is the hope that the ideas can be applied to other classes and applications (for example, in
MathErr
ZeroDivide OutOfBounds General
MathErr . . .
Figure 9.1 Exception class hierarchy
quantitative finance). The interfaces are:
class MathErr
{ // Base class for my current exceptions private:
string mess; // The error message
string meth; // The method that threw the exception public:
MathErr() {
mess = meth = string();
}
MathErr (const string& message, const string& method) {
mess = message;
meth = method;
}
string Message() const { return mess; } string Method() const { return meth; }
virtual vector<string> MessageDump() const = 0;
virtual void print() const {
// This uses a Template method pattern // Variant part
vector<string> r = MessageDump();
// Invariant part
for (int j = 0; j < r.size(); j++)
{
cout << r[j] << endl;
} } };
and the interface for zero divide exceptions is given by:
class ZeroDivide : public MathErr {
private:
string mess; // Extra information public:
ZeroDivide() : MathErr() {
mess = string();
}
ZeroDivide(const string& message,const string& method, const string& annotation )
: MathErr (message, method) {
mess = annotation;
}
vector<string> MessageDump() const { // Full message
vector<string> result(3);
result[0] = Message();
result[1] = Method();
result[2] = mess;
return result;
} };
We wish to use these exceptions in server code. In this case we extend the function sumReciprocalsso that it checks for zero divide:
template <class V> V sumReciprocals(const vector<V>& array) { // Sum of reciprocals
V ans = V(0.0);
for (int j = 0; j < array.size(); j++) {
if (fabs(array[j] < 0.001)) // Magic number!
{
Here we see that the code checks for zero divide conditions and if such an event occurs it will create an exception object of the appropriate type and throw it back to the client. It is then the client’s responsibility to catch the exception and then determine what to do with it. Our first try/catchblock is:
// Now create a try/catch block try
{
double answer = sumReciprocals(myArray);
cout << "Sum of reciprocals: " << answer << endl;
}
catch (ZeroDivide& exception) {
exception.print();
}
In this case the exception will be ‘catched’ in thecatch block and the program continues.
Incidentally, if a server code throws an exception and no try/catch block has been defined in the client code a run-time error will occur and the program will terminate abnormally.
We now investigate the case where the client can decide to repair the situation by giving itself the chance to give a new value for the offending value (namely myArray[5] = 0.0;) or allowing it to exit the try block. To this end, the following code allows the client to give a new value ad infinitum or it can exit by giving the magic number 999:
Lab1: try {
cout << "\ nGive a new value for index number 5:";
double val;
cin >> val;
myArray[5] = val;
if (val == 999.0)
{
return 1; // Exit the program }
double answer = sumReciprocals(myArray);
cout << "Sum of reciprocals: " << answer << endl;
}
catch (ZeroDivide& exception) {
exception.print();
goto Lab1;
}
This completes our first example of the exception handling mechanism in C++. In later chapters we shall use the mechanism in financial engineering applications and test cases.
Summarising, we have introduced the essentials of exception handling in C++ and we have shown how to use it. The knowledge can be applied in your applications.
We have not discussed the following topics (see Stroustrup, 1997 for the details):