Exceptions allow the programmer to handle potential runtime errors in a consistent and robust manner. This refers specifically to errors related to user input, not actual programming errors. For example, if the user tries to find the inverse of a singular matrix, one would expect an exception to be generated. Without exceptions, the programmer has to rely on either testing the input matrix first, or hope that an appropriate error code could be returned from the function. With exceptions, the programmer can simply call the function and assume everything works; if there's an error, they will have an opportunity to either deal with it or let it crash the application.
When a error condition has been detected by a function, it ``throws'' the exception to the caller. The caller can then ``catch'' the exception in a special program block, providing feedback to the user or deal with the error in some way. If the calling function wishes to do this, it places the call in a special try block; if the calling function does not wish to catch exceptions (and therefore allow them to crash the program), then it just calls the function normally.
Here is an example. The code below shows how the try/catch mechanism can be used to handle input errors.
// log_base_2() calculates the base-2 log of the input value. double log_base_2(double value) { if (value <= 0) throw("Error: can't compute log of a non-positive value."); else return log(value) / log(2); } int main() { double input; cout << "Enter your value: "; cin >> input; try { double n = log_base_2(input); cout << "Log base-2 of " << input << " is " << n << endl; } catch (const char * error) { cout << error << endl; } return 0; }The function log_base_2() can fail if the input is non-positive. When this occurs, rather than allowing the math operation to fail (producing non-standard results), the condition is detected and an exception is thrown. In the main() function, the code that assumes the error did not occur is placed inside a try block; if the exception is thrown, the code that occurs after the error in the try block is skipped, and execution jumps directly to the catch block.
Exceptions can be of any type -- that is, what is placed inside the throws() command can be any kind of expression. This allows for great flexibility in error-handling strategies. If a number of different types of errors can occur in a function, for example, then the exception could be an integer whose value has some coded meaning. Furthermore, a function can throw any number of exceptions, all of which can be of different types. The calling function can then selectively catch any or all of the types of exception, by using multiply catch blocks. For example, the following code shows how to catch three types of exceptions that can result from calling a function called foo():
try { foo(); ... // code that assumes foo() succeeds. } catch (int error) { ... // handle int exception types. } catch (double error) { ... // handle double exception types. } catch (const char * error) { ... // handle char* (string) exception types. }(Note that automatic conversion does not occur for exceptions; if the foo() function throws a double, the value will not be converted to an int to allow the first catch block to handle it.)
Sometimes the programmer wants to handle all exception types with the same catch block. This can be done using ... as the catch parameter:
try { foo(); // code goes here that assumes foo() succeeds. } catch (...) { // handle all exception types here }This can also be used in conjunction with other catch blocks; for example, the programmer can catch int exceptions with one block and all other types in another block by placing the int catch block before the ... catch block. Be careful: placing the ... block before other blocks will preempt the other blocks! The catch blocks are processes in order, and the first one matching the exception type will be the one to handle the exception.
In Slate++ , exceptions are used where appropriate. The linear algebra functions that depend on non-singular matrices will throw a const char * exception with an appropriate error message. We are also working on putting proper exception handling in all functions where dimension mismatch could be an issue (e.g. multiplying matrices of incompatible sizes).