Upload
frederick-morris
View
214
Download
0
Embed Size (px)
Citation preview
Exception Handling Programmers must deal with errors
and exceptional situations: User input errors Device errors
Empty disk space, no memory Component errors
Class might perform incorrectly invalid array index Pop() empty stack
Handling errors Most errors are unpleasant to handle The programmer should not just abort
the program with an assertion failure; the user may lose all work performed during the program session
Instead, the program must do the best it can to recover, and exit gracefully, only if it is really necessary.
Reaction to error Conditions Returning an error code
#define FILE_OPEN_ERROR 1#define OK 0
int openMyFile() {FILE * f = fopen(“File.txt”, “r”);if ( f == NULL )
return FILE_OPEN_ERROR;else
/* Do things to a file*/…return OK;
}
Advantage: simple to implementDisadvantage: Code contains a lot of error checking
Do nothing Ignore error condition
Common approach Simpler to code
Hard to debug Program might terminate or abort or produce
incorrect answer Location of original problem might be hard to find Can use assert() in the debug mode Can be misleading to the caller.
void Stack::push() will not push element when stack is full, however, the caller will think he successfully pushed element on the stack
When coping fails, can not simply ignore the problem
Setting an Error Variable You keep a global variable, such as errno Whenever function returns, you can check errno variable
if there was an error when executing the function Simple to implement Code still contains a lot of error handling code, ie:
if ( errno == ERROR_NO_5 ) { printMessage(ERROR_NO_5);} The error report contains only the type of error, not the
name of the offending function Two errors in rapid succession will result in the first one
being overwritten Also a problem with multithreaded functions using the same
variable
Printing an Error Message Reasonable in debugging mode only Simple Impacts reusability
Defeats the purpose of programmatic handling of error conditions
No control of program flow After error is printed, the program continues
Does not always make sense to the user Example: “System error 0xff4ff45”
Aborting the Program Good for fatal error Good for debugging purposes Undesirable loss of all volatile data
accumulated throughout program’s life time
Jumping to an error handler Non-local jump
C setjmp() and longjmp() Provides centralized error handler Do not have to worry about stack
unwinding Good approach for C programs, BAD for C+
+ programs When longjmp() leaves stack of the function, no
destructors are called for the objects allocated on the stack.
Raising a Signal Explicitly creates Software Interrupt by
calling function raise() and specifying interrupt handler
Good for hardware interface Memory fault Segmentation violation
Bad for typical error handling, signals should only be used for their intended purpose
Raising an Exception C++ has a convenient and safe
mechanism to raise exceptions when errors are detected and to transfer both control and error information to code that is competent to deal with the situation
Handles stack unwinding Calls destructors Enables a block of code to be protected Cut down on error handling code
Exceptions Exceptions are the proper way to bail
out of a situation which cannot be handled easily by a function itself, but which are not disastrous enough for the program to terminate completely.
Exceptions provide a flexible layer of flow control between the short-range return and the crude exit()
C++ exceptions When an error is encountered that
cannot be handled at the point of detection, an exception can be raised with the command:
throw e; Where ‘e’ could be an object, structure, or simple data type.Simple data types are not useful:throw 1; throw “Stack underflow”
C++ Exceptions The try block surrounds statements in which
exceptions may be generated Example:
try{
// Statements in which exceptions// may be thrown
} The throw statement should be executed
somewhere within the try-block: either directly or from within a function called directly or indirectly from the try-block
Catch The “catch” keyword immediately follows
the try-block Catch-block receives the thrown
exceptions. Example
try {read();
} catch( StackError s){
// Report syntax error in the input file, and close file}
Throwing exceptions in C++ Local objects should not be thrown, nor should pointers to
local objects be thrown However, it is possible to throw pointers or references to
dynamically generated objects, taking care that the generated object is properly deleted when the generated exception is caught
Example:
try{throw new Exception();
}catch( Exception * e) {
// process edelete e; // deallocate dynamically created object
}
Throwing references We could thrown referencestry {
throw *new Exception();}catch( Exception & e) {
// Process edelete &e;
}
Catching references / Safe This is totally safe
try {throw Exception(); }
catch(Exception & e) {//
}Output:
Exception() constructor is calledcatch block is processed~Exception() is called when reach end of catch-block
Catching exceptions / Unsafe This is not always safe depending on the nature of
Exception destructortry {
Exception e;throw e; }
catch(Exception & e) {//
}Output:
Exception() constructor is called~Exception() destructor is calledcatch block is processed~Exception() is destructor is called the second time
- Potential problem of double deallocation
Handling multiple exceptions A try block can throw different
types of exceptionstry { /* code */ }catch( StackError & s ) { // Process StackError}catch(MathError & m ) {
// Process MathError ) }
rethrow Occasionally, we want to catch any exceptions that fly
past a certain point, just to take some cleanup or protective actionstry { /* … */ }catch( … ) { /* evasive action */throw;
}The throw command without an argument in-side a catch
clause throws the current error again Errors that current function does not know how to
handle must be re-threw
Declaration of exception throwers The declaration of a function that may throw one or
more exceptions:void exceptionThrow() throw(char *, int);
This function may throw char * and int exceptions Note, declaration of exceptions is not mandatory
However, it declared, it notifies the user of a function that this function may throw certain exceptions, which need to be handled
Without a function throw list all responsibilities of providing the correct handlers is in the hands of the designer of the program
Derived exceptions Lets assume class D derives from
class B and exception of type D is thrown, then we can catch exception D using catch(B) {
/* Catch exception B and any classes deriving from it */}Note: Catch block can only specify the type
Stack unwinding The process of searching “up though
the stack” to find a handler for an exception is commonly called “stack unwinding”
All objects on the stack between the throw and catch point are properly destroyed by invoking their destructors This activity is of crucial importance, it is
guaranteed that all destructors of abandoned stack objects are invoked thus preventing memory leak
Stack unwinding
main()
a()
b()
c()
d()
e()Top of the stack
throw Exception
Catch Exception
Stack unwinds starting from function e(), d(), and c(). Destructors are called in e(), d(), and c() as stack unwinds, until exception is caught at b()
Destructors and uncaught exceptions Uncaught exceptions cause
program Abort and exit Destructors should not throw
exceptions If a destructor that is invoked while
an exception is pending itself raises an exception, the program terminates
Exception advices Don’t throw exceptions from libraries
Customers will not know that they need to catch an exception
When they catch an exception they will not know how to handle it
If you have to throw an exception, specify it in the prototype of the function This makes the user of the function aware that
he might need to catch an exception Never throw exceptions to return the result
of computation.
What exceptions should you throw? Failure of precondition Resource limitation Device failure Subsystem failure
More advice Catch those exceptions that you can
handle Catch and rethrow exceptions that you
can partially handle If possible, don’t use pointers and
handles on the stack but place then inside objects with proper destructors
Don’t throw an exception if you can continue