10.3 SOME USAGE PATTERNS FOR EXCEPTION HANDLING IN C


10.3 SOME USAGE PATTERNS FOR EXCEPTION HANDLING IN C++

There is obviously something to be said for learning a new language construct by simply seeing a variety of the usage patterns for the construct. This section shows some of the different ways in which the try-catch mechanism can be made to work for handling exceptions in C++. Our list of the usage patterns is not exhaustive by any means, but should be good enough for starters.

Usage Pattern 1:

Here is what could perhaps be called the simplest possible example of using the try-catch mechanism in C++:

 
//ExceptionUsage1.cc #include <iostream> using namespace std; void f() { throw 1; } //(A) int main() { try { f(); //(B) } catch( int ) { cout << "caught it"; } //(C) return 0; }

The function f is written in line (A) to throw an int of value 1. This function is invoked in main in line (B) inside a try clause, followed by a catch clause where the object thrown is caught. Note that the parameter of the catch clause in line (C) only mentions the type of the object expected to be caught.

Usage Pattern 2:

While the previous example used a primitive type for the throw, this example, like the program TryCatch.cc presented earlier, shows the use of a class type for the same purpose.

 
//ExceptionUsage2.cc #include <iostream> using namespace std; class MyException {}; void f() { throw MyException(); } //(A) int main () { try { f(); //(B) } catch( MyException ) { //(C) cout << "caught MyException"; } return 0; }

The function f is written so as to throw a class type exception. The object thrown is of type MyException. The syntax MyException() in line (A) is an invocation of the system-supplied default no-arg constructor for the MyException class. The system supplies a no-arg constructor since the programmer did not provide any constructors for this class. (No-arg constructors are discussed in Chapters 7 and 11.) As before, f is invoked inside a try clause in line (B). As for the previous example, note again that in line (C) we only show the type of the exception object to be caught.

Usage Pattern 3:

The usage example below is identical to the previous one, except for the header of the function f. By incorporating the clause throw (MyException), the header now declares explicitly the type of exception this function will throw. The throw clause in the header of the function is referred to as its exception specification. When a function is given an exception specification, it is a guarantee that if the function throws an exception, the exception will be of the type mentioned in the exception specification.[04] When a function header carries no exception specification, it means that the function can throw an exception of any arbitrary type. And when a function is given an empty exception specification with the clause throw(), the function is expected to throw no exceptions at all. It is recommended that the reader get into the habit of incorporating exception specifications in the headers of exception throwing functions.[05]

 
//ExceptionUsage3.cc #include <iostream> using namespace std; class MyException {}; void f() throw(MyException) { throw MyException(); } //(A) int main() { try { f(); } catch(MyException) { cout << "caught MyException"; } return 0; }

Usage Pattern 4:

It is possible for the same function to throw multiple exceptions in response to different conditions encountered during its execution. In the following usage pattern, the function f is written to throw two different exceptions, one of type MyException and the other of type Err. This fact is declared in the header of the function in line (A).

 
//ExceptionUsage4. cc #include <iostream> using namespace std; class MyException {}; class Err {}; void f(int j) throw(MyException,Err) { //(A) if (j == 1) throw MyException(); if (j == 2) throw Err(); } int main() { try { f(1); } catch (MyException) { cout << "caught MyException -- arg must be 1" << endl; } catch (Err) { cout << "caught Err -- arg must be 2" << endl; } try { f(2); } catch( MyException) { cout << "caught MyException -- arg must be 1" << endl; } catch(Err) { cout << "caught Err -- arg must be 2" << endl; } return 0; }

Usage Pattern 5:

This usage pattern shows that a calling function can re-throw a caught exception. The exception re-thrown can be the same as the one that is caught, or it can be a different object.

In the following example, the function f is written in line (A) so as to throw an exception of type MyException. The function g of line (B) calls f and traps the MyException object thrown by f in line (C). In response to this trapping, g throws an exception of type Err in line (D).

 
//ExceptionUsage5 . cc #include <iostream> using namespace std; class MyException {}: class Err {}; void f() throw(MyException) { throw MyException();} //(A) void g() throw(Err) { //(B) try { f(); } catch (MyException e) { //(C) cout << "oh my" << endl; throw Err(); //(D) } } int main() { try { g(); } catch (Err) { cout << "caught Err" << endl; } return 0; }

Usage Pattern 6:

The function that traps the exception does not have to be the one that invokes the exception-throwing function. The exception can be caught by some other function higher up in the function stack.

The following usage example is similar to the previous one except that the exception-throwing function g of line (B) is invoked by another function h of line (C) that does not care to trap the exception thrown by g. Despite the fact that h ignores the exception thrown by g, when h is invoked in line (D) of main, the exception thrown by g can still be trapped in main.

 
//ExceptionUsage6. cc #include <iostream> using namespace std; class MyException {}; class Err {}; void f() throw( MyException ) { throw MyException(); } //(A) void g() throw (Err) { //(B) try { f(); } catch (MyException e) { cout << "oh my" << endl; throw Err(); } } void h() { g(); } //(C) int main() { try { h(); //(D) } catch( Err ) { cout << "caught Err" << endl; } return 0; }

Usage Pattern 7:

A function can carry an exception specification even if its implementation code does not explicitly contain a throw clause. To illustrate, if in the previous example we wanted to make explicit the fact that h will throw an exception of type Err if such an exception were to be generated by the implementation code for h(), we can state so explicitly in the header of h, as in line (A) of the following usage pattern:

 
//ExceptionUsage7 .cc #include <iostream> class MyException {}; class Err {}; void f() throw( MyException ) { throw MyException(); } void g() throw(Err) { try { f(); } catch( MyException e ) { cout << "oh my" << endl; throw Err(); } } void h() throw(Err) { g(); } //(A) int main() { try { h(); } catch (Err) { cout << "caught Err" << endl; } return 0; }

Usage Pattern 8:

The purpose of this pattern is to show that the prototype of an exception-throwing function must include a throw clause if such a clause is included in the header of the function definition, as shown in lines (A) and (B) below.

 
//ExceptionUsage8.cc #include <iostream> class MyException {}; void f() throw(MyException); //(A) int main() { try { f(); } catch( MyException ) { cout << "caught MyException"; } return 0; } void f() throw(MyException) { throw MyException(); } //(B)

Usage Pattern 9:

In the previous examples we threw empty objects for exceptions. But that obviously would not serve a very useful purpose in real programming. In practice, you'd want an object thrown to carry with it some useful message to the point where the exception is trapped. The following usage example illustrates this.

 
//ExceptionUsage9.cc #include <iostream> #include <string> class MyException { //(A) string message; //(B) public: MyException( string m ) { message = m; } //(C) string getMessage() { return message; } } void f() throw( MyException ); int main() { try { f (); } catch( MyException ex ) { cout << ex.getMessage(); } //(D) return 0; void f() throw( MyException ) { throw MyException( "hello there" ); //(E) }

The class MyException in line (A) now has a data member, message, shown in line (B), that can be set to the message that the exception object must carry with it when it is thrown. For this purpose, the class has been provided with a constructor, as shown in line (C). In line (E), when we construct the exception object, we provide it with a message as the argument to the constructor. This message is retrieved in line (D) when the exception is trapped.

[04]However, in practice, a function may violate this guarantee by throwing an exception that is not listed in the exception specification. When that happens, the function std::unexpected() is invoked whose default behavior is to call std::terminate() that in turn calls abort().

[05]As we will later in this chapter, incorporating such declarations in exception throwing functions is mandatory in Java.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net