Exceptions
n Programmers have traditionally ignored the proper dedication of attention to error handling n Enclosing code inside countless nested-ifs is not an attractive approach n This approach is not perfect either. n It does minimize the attention you (the programmer) have to give to errors once the appropriate infrastructure has been established. n Once again it has subtleties which require the programmer to be careful. n Should be used sparingly on MAJOR errors.
EXCEPTIONal circumstances n Determining WHEN to use exceptions is difficult and subjective. n Most authors tend to agree that they are to be used in exceptional circumstances – memory allocation problems – input file incorrectly formatted – dividing by zero – removing from an empty stack n In the first example, a rational number class will be examined. The primary errors here may not be exceptional but will serve to indicate the principles.
Current approaches include Use the assert macro to display an error char* newstring = char[stringlen+1]; assert(newstring != NULL); -> Assertion failed: newstring != NULL; n Surrounding with an if if (newstring ==NULL) {...; exit(-1);}..... n For classes, you can install an error handler as shown on p. 313 of Horstmann. (considered later)
THREE basic parts of exceptions n TRY - a block of code in which exceptions can occur n THROW - a call to an exception handler n CATCH - a routine to handle the problem n Example: try{ // code causes error.... throw Error1; // int } catch(int code) {...// handle error } try{.. if(condition) throw... } catchhandler{ handle & stop or continue } EXIT
Simple example char inputint; cin >> inputint; try { if (!isalpha(inputint)) throw (int) inputint; // process normal char } catch(int errorval) { inputint = FILLCHAR; } // here ignore errorval // continues execution CLEARLY THIS WOULD BE HANDLED ANOTHER WAY BUT IT REPRESENTS A SIMPLE EXAMPLE
Multiple handlers are used in order and by type char inputint; cin >> inputint; try { if (!isalpha(inputint)) throw (int) inputint; else throw inputint; } catch(int errorval) { // catch must immed follow try cout << “Error int ” << errorval << endl;... } catch(char errorval) { cout << “Good char ” << errorval << endl; } 5 Error int 5 68 Good char D
Flow control: throw or f(x) which throw s try { throw or f(x) more statements in try } catch( ) { statements; exit or throw or neither } statements f(x) { throw..; other statements in f(x); } PROGRAM EXITS To an enclosing nested try..catch. I.e. forward the error to another handler. more statements in try & other statements in f(x); MAY NOT EXECUTE!
Better example class Ratnl { private: long n; long d; public: Ratnl(long,long); Ratnl(long); }; Ratnl::Ratnl(long a,long b) {if (a==0) throw(RatnlExcept(ZeroNum)); if (b==0) throw(RatnlExcept(ZeroDen)); n=a; d=b; } enum RatnlErr{ ZeroNum, ZeroDen;}; ZeroNum, ZeroDen;}; class RatnlExcept{ RatnlError err; RatnlError err; public: public: RatnlExcept(RatnlErr c) RatnlExcept(RatnlErr c) { err = c;} { err = c;} void Response() void Response() {switch (err) {switch (err) case ZeroNumer: case ZeroNumer: cout << “0 Numer\n”; cout << “0 Numer\n”; break; break; case ZeroDenom: case ZeroDenom: cout << “0 Denom\n” } cout << “0 Denom\n” }}; From WORX text The Beginner’s Guide to OOP Using C++ by Romanovskaya, et all
The main routine void main() {.... try { Rational r(3,4); Rational s(7,0); cout << “Construction done!”; } catch (RatnlExcept & r) { r.response(); } cout << “End of Program\n”; } OUTPUT: 0 Denom End or Program NOTE: The programmer decided to catch the error. Failure to catch is also a problem.
The throw statement n IMPLICIT THROW : You call a routine which throws an exception. If you have the call within a try block, the catch associated with the try containing the call may handle the error. ( The main routine overhead) – The called routine CAN catch the error. – If it does, it can also choose to throw the error to an enclosing catch handler. » p. 318 » rethrow same exception with no parameters throw; n EXPLICIT THROW: The try itself call throw. ( Simple example overhead)
The catch function Catch function must match parameters Catch functions are used from the innermost to outermost try blocks. n Note the try blocks are dynamically defined n Within a block, the order (matching) is the physical order. n The first parameter match found is the one used (first). n Catch can have (...) ellipses for parameters – This handler matches ANY call n Catch can have NO type: only handles no parameter.
#include void main() { int t=5; try { if (t==5) throw 3; cout << "No error\n"; } catch (int z) {cout << "Value caught was " << z << endl;} cout << "Program done\n"; } OUTPUT : Value caught was 3 Program done
#include void main(){ int t=5; try { try { if (t==5) throw 3; cout << "No error\n"; } catch (int z) {cout << "Value caught was " << z << endl;} } catch (int x) {cout << "Value caught OUTER was " << x << endl;} cout << "Program done\n"; } OUTPUT: Value caught was 3 Program done Catch function IS NOT CALLED !
void main(){ int t=5; try { try { if (t==5) throw 3; cout << "No error\n"; } catch (int z) {cout << "Value caught was " << z << endl; throw; // THROW IT TO OUTER HANDLER } catch (int x) {cout << "Value caught OUTER was " << x << endl;} cout << "Program done\n"; } OUTPUT: Value caught was 3 Value caught OUTER was 3 Program done
void main(){ int t=5; try { try { if (t==5) throw 3; cout << "No error\n"; } catch (int z) {cout << "Value caught was " << z << endl; throw; // THROW IT TO OUTER HANDLER } catch (...) {cout << "In Catchall" << endl; throw;} } catch (int x) {cout << "Value caught OUTER was " << x << endl;} catch (...) {cout << "In SECOND Catchall" << endl;} cout << "Program done\n"; } OUTPUT: Value caught was 3 Value caught OUTER was 3 Program done NEITHER (...) functions are used even with throw; Only one of the handlers is used at a level.
void main() { int t=5; try { try { if (t==5) throw 3; cout << "No error\n"; } catch (int z) {cout << "Value caught was " << z << endl; throw; } catch (...) {cout << "In Catchall" << endl; throw;} } catch (int x) {cout << "Value caught OUTER was " << x << endl; throw; // THROW IT TO OUTER HANDLER } catch (...) {cout << "In SECOND Catchall" << endl;} cout << "Program done\n"; } OUTPUT: Value caught was 3 Value caught OUTER was 3 Abnormal program termination The last throw could NOT be caught and resulted in termination!
catch(...) Always use catch(...) last try { } catch (...) { } catch ( int x ) { } n The last handler will never be used because the first handler will always catch the exception.
Control over who throw s Any exception throw n but not caught results in abnormal program termination! You want knowledge (minimally) and control (optimally) over who throw s. resulttype f(parameters) throw (throwlist); Ratnl f(long x, long y) throw (RatnlExcept); This determines what exceptions can be thrown by this function!... a throwlist Any other throw results in program termination. throw() as a throwlist intercepts the throw and does not allow it outside the function, but short-circuits straight to program termination, unless the function ITSELF has a catch for the throw.
Types of exceptions n If an exception occurs which is not listed in the throw list -> UNEXPECTED EXCEPTION – c++ has a function unexpected() which handles the error. – you can redefine it. » Yours-> void myunxp() ; {... ; terminate(); // called by system one} » call -> set_unexpected(myunxp); n If no matching handler is found -> UNHANDLED EXCEPTION – Normally calls terminate(); as shown above – redefine by » call -> set_terminate(myterminate); – your terminate() should call either »exit() or abort()
Constructors and Destructors n If the code is skipped in a try block, what about the destructors? n When an exception is thrown inside a try block, the destructor for each and every object created in the try block is called before the catch handler gets control. class Ratnl{... ~Ratnl() // destructor { cout << “Rat done!” << n << ‘/’ << d << ‘\n’;} }; void main() { try { Ratnl r(3,5); Ratnl s(2,0); }... } OUTPUT:... Rat done 3/5
Where should try begin/end? n Considering the constructor/destructor issue: n Consider the group of items which are involved in an operation n Declare objects in the try block when possible. n When the operation fails, destructors are automatically called. Some can, others can’t. fs istream; fs.open(“datafile”); g(fs); // causes exception fs.close(); // not called fs.open(“datafile”); try { Employee e; f(fs);} catch(...) { fs.close(); throw; } fs.close(); NOTE: in this example, fs COULD be placed inside
Catch routines with references to base/derived classes n Given a base class B and derived class D: n Assume that each class has a routine defined for you to use as a catch routine (part of the class). catch ( B& b) {..... } catch ( D& d) {..... } n Can the second ever be reached? NO. A B reference will use the first A D reference will be converted to a B reference
A Design Strategy n When designing your own class libraries provide the programmer an opportunity to supply a handler. n Here I use the term handler to indicate routine which throws not the routine which catches. n P.313 Stack shows how to – define a default handler – let the programmer set their own handler – let the programmer reset the handler to the original – user version can only access public info n The default handler can throw exceptions n The programmer substituted handler can avoid throwing the error.
Static Member Functions Static member functions in Stack: n Function is for the whole class n “this” is not available n only static data members and static functions can be accessed
Design Suggestions n Don’t catch it if you can’t handle it. n Don’t use exceptions for simple errors easily handled with direct code. n You want to continue if possible. Avoid throwing if you can continue from where you are. n Give users of libraries some control over handlers n Package items in the try section to be in objects which have properly defined destructors so that they are properly disposed in the event of exceptions thrown.