Internal errors are due to faulty logic or coding in the program. Common types of internal errors include:
Bounds errors;
Inserting a null pointer into a collection;
Attempting to use a bad date.
All of these errors should be preventable. For example, you always know the permissible range of indices for an array, so you can probably avoid a bounds error. You would correct your program's use of a bad date as an obvious logic error.
Internal errors can be further classified according to the cost of error detection, and whether or not the error will be detected at run time. The two categories are:
Non-recoverable internal errors;
Recoverable internal errors.
Non-recoverable internal errors share the following distinguishing characteristics. They are:
Easily predicted in advance;
Encountered at relatively low levels;
Costly to detect;
Detected only in the debug version of the library.
Non-recoverable internal errors by definition have no recovery mechanism. Examples of these errors include bounds errors, and inserting a null pointer into a collection.
Why does a library define some errors as unrecoverable? Because detecting errors takes time. For performance reasons, a library demands some minimal level of correctness on the part of your program, and pitches anything that falls short. Errors are non-recoverable in the sense that the production version of the library has no mechanism for detecting such errors and, hence, no opportunity for recovering from them.
Bounds errors are non-recoverable because the cost of checking to make sure an index is in range can well exceed the cost of the array access itself. If a program does a lot of array accesses, checking every one may result in a slow program. To avoid this, the library may require you to always use a valid index. Because a minimum level of correctness is demanded, non-recoverable errors are simple in concept and relatively easy to avoid.
You can best discover and eliminate non-recoverable errors by compiling and linking your application with the debug version of the library. See Section 17.5 for details. The debug version includes lots of extra checks designed to uncover coding errors. Some of these checks may take extra time, or even cause debug messages to be printed out, so you will want to compile and link with the production version for an efficient final product.
If the debug version of the library discovers an error, it typically aborts the program.
Recoverable internal errors are similar to their non-recoverable relatives in that they are easy to predict and occur at low levels. They differ in that they are:
Not costly to detect;
Detected in both the debug and the production versions of the library.
A bounds error in a linked list or an attempt to use an invalid date are both examples of recoverable internal errors. The library's response to these errors is to throw an exception inheriting from RWInternalErr.
The production version of the library can check for recoverable internal errors because the cost is relatively low. For example, to find a bounds error in a linked list, the cost of walking the list will far exceed the cost of detecting whether the index is in bounds. Hence, you can afford to check for a bounds error on every access.
If an error is discovered, the library will throw an exception inheriting from RWInternalErr, as we have mentioned. Here's what it looks like when Tools.h++ throws an exception:
// Find link "i"; the index must be in range: RWIsvSlink* RWIsvSlist::at(size_t i) const { if (i >= entries()){ if(RW_NPOS == i) RWTHROW( RWBoundsErr( RWMessage( RWTOOL_NPOSINDEX))); else RWTHROW( RWBoundsErr( RWMessage( RWTOOL_INDEXERR, (unsigned)i, (unsigned)entries()) )); } register RWIsvSlink* link = head_.next_; while (i--) link = link->next_; return link; }
In this code, note how the function always attempts to detect a bounds error. If it finds one, it throws an instance of RWBoundsErr, a class that inherits from RWInternalErr. This instance contains an internationalized message, discussed in Section 16.2. The RWTHROW macro is discussed in Section 17.4.1.
Throwing an exception gives you the opportunity to catch and possibly recover the exception. However, because the internal logic of the program has been compromised, most likely you will want to attempt to save the document you are working on, and abort the program.