An exception refers to an instance that does not conform to a rule or generalization. It is an indication of a problem that may occur during program execution. The mechanism of exception handling provides a way to transfer control and information from the point of its occurrence during execution to the point where a handler routine is written. The handler routine gives the programmer a window of opportunity to succumb to a graceful termination or to deal with it in an appropriate manner. This article gives a basic idea on the implementation of exception handling mechanism in C++.
Overview
A program begins by performing a certain task. If the code to perform the specific task runs smoothly, the next task is performed, and so on until the program completes. Now, if there is any error in the course of its execution, the code falls back to an error handling routine. The error processing logic mingles with program logic and hops between them. Although this form of intermixing is fine, it makes the code difficult to read, maintain, and debug, even for a moderately sized program.
Note that the provision of the error handling mechanism in C is very primitive. There is no direct support from the language itself. Therefore, what we do here is accesses a low-level form of return values of functions. Conventionally, if a negative value or NULL is returned by a function, it indicates an error. Sometimes, a global variable called errno is set as an error indicator during function calls. The header file error.h defines many such error codes.
Using exceptions for error handling brings clarity and makes the code simpler by decoupling the exception routine from the program logic. We can deal with them separately. This brings immense flexibility; apart from this, we can decide to handle exceptions of our choice, group them into related types, or completely ignore them at our discretion.
Applying Exception Handling in C++
C++ exceptions are encompassed within a try…catch block. The try block encloses the code where an exception may occur. It is defined by the keyword try, followed by braces. If an exception occurs, it is processed by the catch handler that catches and handles the exception. The catch portion is also a keyword followed by braces. Every try block is accompanied by at least one catch handler that immediately follows it. There may be more than one catch handler associated with one try block. In such a case, when an exception occurs, the appropriate catch handler executes one whose type matches with the type of exception that occurred. The matching is done on the basis of the exception that is thrown or according to whose base class it inherits.
An example to illustrate the basic idea is as follows:
#ifndef DIVISIONBYZEROEXCEPTION_H_ #define DIVISIONBYZEROEXCEPTION_H_ #include <stdexcept> using namespace std; class DivisionByZeroException :public runtime_error{ public: DivisionByZeroException():runtime_error ("divide by zero exception occurred"){}; }; #endif /* DIVISIONBYZEROEXCEPTION_H_ */ #include <iostream> #include "DivisionByZeroException.h" using namespace std; double div(int num, int denom) { if (denom == 0) throw DivisionByZeroException(); return static_cast<double>(num) / denom; } int main() { int n1, n2; double result; cout << "Enter two number: "; cin >> n1 >> n2; try { cout << "The result is " << div(n1, n2) << endl; } catch (DivisionByZeroException &ex) { cerr << "Excetion: " << ex.what() << endl; } return 0; }
Exceptions are designed to handle only synchronous errors, such as accessing the index of an array out of bounds, division by zero, arithmetic overflow, fail to allocate memory, and so forth. Asynchronous exception, such as keyboard and mouse interrupts, network message receipt, I/O completion, and so on, cannot be handled because these events occur in parallel, independent from the program control flow.
An Exception Re-thrown
A program can decide to partially handle an exception or not handle it at all. In such a case, it can delegate the responsibility to another handler.
void func1() { try { throw exception(); } catch (exception &) { throw; } } int main() { try { func1(); } catch (exception &) { } return 0; }
The function func1 is invoked inside the try block in main. The function func1 also contains a try…catch block. The throw statement throws an instance of a standard library class exception, called exception. The catch handler (of func1) catches this exception and throws it again, which then is caught by the catch block defined in main. The catch block in main does nothing with the exception and terminates the program.
Exception Specification
We can create a throw list for a function that enumerates a list of exceptions.
void func1() throw(Exception1, Exception2, Exception3){ // ... }
This function can throw only the exception specified with the throw. If an exception that is not specified in the throw list occurs, the exception handling mechanism invokes the unexpected function and terminates the program. However, if we do not provide a throw list as follows, it automatically invokes the unexpected function. (The behavior of the unexpected function can be customized with the set_unexpected function. Refer to the C++ Standard documentation for more information on this).
void func1() throw(){ // ... }
Conclusion
Most modern programming languages have the capability of exception handling as one of its features. Some languages, such as Java, is built keeping this in mind from the very outset. Due to this, using exception handling mechanism is quite common. C++, on the other hand, is a reconstruct of C; exception handling is a later addition. Moreover, this capability had to be incorporated in such a way to not disturb the already existing vast majority of code, yet utilize the benefit of this feature. Therefore, the design decision of exception handling had several corners to look into before incorporating it into actual implementation. The article Exception handling for C++ by Andrew Koenig and Bjarne Stroustrup lays down the foundation and provides important insight into its design principle.
Reference
Exception Handling for C++ by Andrew Koenig and Bjarne Stroustrup