180 likes | 194 Views
This article provides an introduction to exception handling in programming, including its definition, motivation, and syntax. It explains the need for exception handling and how to use try-catch blocks to handle exceptions. It also covers topics such as throwing objects, stack unwinding, re-throwing, catch-all clauses, and exception specifications.
E N D
Introduction to Exception Handling • Definition: An exception is any unusual event, either erroneous or not, detectable by either hardware or software, that may require special processing • Without exception handling • When an exception occurs, control goes to the operating system, where typically • an error message is displayed • the program is terminated • With exception handling • Programs are allowed to trap exceptions • There is a possibility to fix the problem and continuing execution
Possible Naïve Solutions • Exit the Program • Isn’t it too strong • OS already does the same • Return Error Code • Need to check the return code for every call • Raise a Flag • Need to check the flag after every operation • Call a Special Code to Correct the Error • But when shall we call it?
Motivation • A mechanism which allows two components to survive runtime program anomaly • Encountering such an anomaly (division by zero, access to an array outside its bound, etc.), may need an immediate handling • The handling may reside in a different component, which we need to communicate • The encountering components throws an exception • The handling code, catches the exception, and handles it accordingly
Example • Lets consider class stack const int MAX_ELEM=10; template <typename T, int size=MAX_ELEM> class Stack { T m_arr[size]; int m_index; public: Stack() : m_index(0) {} void Push(T elemP) { m_arr[m_index] = elemP; m_index++; } T& Pop() { m_index--; return m_arr[m_index]; } int Index() const { return m_index; } };
Exception Handling Syntax • Exception Handlers try { -- code that is expected to raise an exception } catch (formal parameter) { -- handler code } ...
Class Anomalies • What happens if we pop() an empty stack? • What happens if we push() on a full stack? • We decide that these situations need to be handled in a special manner, since they are anomalies of the class behavior. • First we define classes of exceptions which may be thrown • Upon anomaly detection, we will throw an exception class popOnEmpty { /* … */ } class pushOnFull { /* … */ }
Example • Our code would now change • No need to examine the returned value for checking for success. … void Push(T elemP) { if (m_index>=size) throw pushOnFull(); // an object is throws m_arr[m_index] = elemP; m_index++; } T& Pop() { if (m_index<=0) throw popOnEmpty(); // an object is thrown m_index--; return m_arr[m_index]; } … };
Try…Catch Blocks • Wrap the code which needs to be “exception sensitive”, and react to raised exceptions, in a try {…} catch {…} blocks • The client’s code look now as following int main() { try{ Stack<int> si; si.Pop(); si.Push(5); si.Pop(); } catch (popOnEmpty) { cout << "stack is empty !!" << endl; } };
Try…Catch Blocks • When exception is thrown, execution resumes in the “closest” catch clause handling the exception. • If no catch clause exists capable of handling the exception, program execution resumes at the terminate() function. • Variables declared within the try block cannot be referred to at the catch clause, since they are local to the block. int main() { try{ Stack<int> si; si.Pop(); si.Push(5); si.Pop(); } catch (popOnEmpty) { cout << "stack is empty !!" << endl; } cout << “Execution resumes here” << endl; }; Execution does Not resume here Type declaration
Throwing Objects • Why throw an object? • Additional data encapsulated • An object is created upon invoking the throw statement • Can be created with additional data, passed to Ctor • Object is destroyed after the catch clause ends
Stack Unwinding • Up the chain of nested function calls, a suitable catch clause is searched for • Upon functions and compound statements exit, stack is being unwound • Lifetime of local variables ends • Destructors are called • If no handler is supplied, terminate() is called • Since exception is a situation, where the program cannot continue execution in a normal manner • By default terminate() calls abort() • Similar to function call behavior • But information to set up a function call is not available at compile time, run-time support is needed
Re-throwing and Catch All • After some corrective action, a catch clause may throw an exception to be further handled, passing the exception to another catch clause, by throw. • General exception handling can be specified catch(…) { // place code here } • If specified with combination, must be specified last, since evaluation is evaluated in turn.
Exception Specification • Function interface need to be as precise as possible, it is the contract between several codes • Client code may need to prepare for specific handling • Best is to provide exception specification with the method declaration • This will help the compiler to check consistency, to check that no other exceptions are thrown • It follows the function parameter list
Exception Specification … void Push(T elemP) throw(pushOnFull) { if (m_index>=size) throw pushOnFull(); // an object is throws m_arr[m_index] = elemP; m_index++; } T& Pop() throw(popOnEmpty) { if (m_index<=0) throw popOnEmpty(); // an object is thrown m_index--; return m_arr[m_index]; } … };
Nesting try Blocks • Exceptions are always handled by closest matching handler try { try { throw 5; } catch (int x) { cout << x << endl; // exception will be caught here } } catch (int x) { cout << x-5 << endl; }
Exceptions from Function Calls • No different from nested try blocks • Auto variables on stack are destroyed • Exception jumps to nearest handler • Allows exception handlers to be implemented far away from where the problem was • May requires a knowledge of the issue packaged in exception object for appropriate handling
Exception Hierarchies • Class types representing exceptions may be organized into groups or hierarchies Class Excp {} Class stackExcp : public Excp {} Class popOnEmpty : public stackExcp {} Class pushOnFull : public stackExcp {} • Throwing an exception of type popOnEmpty may be caught by a handler expecting its base • Therefore order should be handling more concrete types, and then handling more generic types • Since handling is evaluated in order of specification, “first match” • Ctors and Dtors are called in order like before