FAQ 9.07 Why is it helpful to separate normal logic from exception handling logic?

The program is easier to read because normal logic doesn't get lost in the error-handling code.

Consider the following Matrix class.

 #include <iostream> #include <string> using namespace std; class Matrix { public:   Matrix() throw();   // ... }; 

Suppose the goal is to create a routine that will add, subtract, multiply, and divide two matrices (assuming a suitable definition for matrix division). The routine is supposed to handle any overflow condition by printing a message to cout, but it is supposed to report any underflow condition back to the caller. Two solutions are presented here: the first uses C++ exceptions and the second uses return codes.

The following low-level routines that perform the arithmetic would be an ideal example for operator overloading, but in an effort to keep the two solutions as similar as possible, normal named functions are used instead. The header <stdexcept> declares the standard C++ exceptions overflow_error and underflow_error.

 #include <stdexcept> using namespace std; Matrix add(const Matrix& a, const Matrix& b)              throw(overflow_error,underflow_error); Matrix sub(const Matrix& a, const Matrix& b)              throw(overflow_error,underflow_error); Matrix mul(const Matrix& a, const Matrix& b)              throw(overflow_error,underflow_error); Matrix div(const Matrix& a, const Matrix& b)              throw(overflow_error,underflow_error); 

The routine that does the actual work, solutionA(), is defined as follows. As specified, the routine handles overflow errors by printing a message but doesn't handle underflow errors, instead (implicitly) passing the underflow exception back to its caller.

 void solutionA(const Matrix& a, const Matrix& b)                  throw(underflow_error) {   try {     cout << "a + b is " << add(a, b) << '\n';     cout << "a - b is " << sub(a, b) << '\n';     cout << "a * b is " << mul(a, b) << '\n';     cout << "a / b is " << div(a, b) << '\n';   }   catch (overflow_error& e) {     cout << "overflow: " << e.what() << '\n';   } } 

Now consider the same situation using return codes. First the four arithmetic routines are declared, as before. However, this time two separate return values are needed: the Matrix result and the return code that indicates whether there is an error. In this case the Matrix result is passed by reference and the return code is returned, but these could be reversed easily. The return code has three potential values: OK, OVERFLOW_ERROR, and UNDERFLOW_ERROR:

 enum ReturnCode {   OK,   OVERFLOW_ERROR,   UNDERFLOW_ERROR }; ReturnCode add(Matrix& result, const Matrix& a,                const Matrix& b); ReturnCode sub(Matrix& result, const Matrix& a,                const Matrix& b); ReturnCode mul(Matrix& result, const Matrix& a,                const Matrix& b); ReturnCode div(Matrix& result, const Matrix& a,                const Matrix& b); 

Up until this point the return code technique is not substantially more (or less) complex than the technique that uses C++ exceptions. However, the code that uses the arithmetic routines is much more complex when return codes are used. This routine needs to explicitly check the return code from each of the arithmetic calls. This tends to mix the normal logic with the error handling logic:

 ReturnCode solutionB(const Matrix& a, const Matrix& b) {   Matrix result;   ReturnCode rc;   rc = add(result, a, b);   if (rc == OK) {     cout << "a + b is " << result << '\n';   } else if (rc == OVERFLOW_ERROR) {     cout << "overflow error: Matrix + Matrix\n";     return OK;                                       <-- 1   } else {     return rc;                                       <-- 2   }   rc = sub(result, a, b);   if (rc == OK) {     cout << "a - b is " << result << '\n';   } else if (rc == OVERFLOW_ERROR) {     cout << "overflow error: Matrix - Matrix\n";     return OK;   } else {     return rc;   }   rc = mul(result, a, b);   if (rc == OK) {     cout << "a * b is " << result << '\n';   } else if (rc == OVERFLOW_ERROR) {     cout << "overflow error: Matrix * Matrix\n";     return OK;   } else {     return rc;   }   rc = div(result, a, b);   if (rc == OK) {     cout << "a / b is " << result << '\n';   } else if (rc == OVERFLOW_ERROR) {     cout << "overflow error: Matrix / Matrix\n";     return OK;   } else {     return rc;   }   return OK; } 

(1) Overflow has been handled so return normally

(2) Some other error such as Underflow

In this case, the normal algorithm gets lost in the code for error detection and error recovery.



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

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