9.3 Error Reporting and Error Handling
Error reporting in C++ is often accomplished through the use of exceptions. When an error condition is encountered, an application can throw an exception to report the error condition, as follows:
 
if (errorCondition)
{
throw E;
}
“E” in the above throw statement can be any type. One may choose simply to throw an integer error code, or to use a customized error class providing detailed information regarding the error condition. When such exceptions are thrown and not specifically caught, the application in which the exception is thrown abnormally ends. To avoid abnormal program terminations, you should surround code that could throw an exception with a “try-catch” block, as follows:
 
try
{
possibleException();
}
catch (E e)
{
handleException(); }
The code that may throw an exception is placed within the try block. If an exception of type “E” is thrown, then it may be “caught” in the catch block that follows. If the type of expression in the catch statement is not the same as the type of a thrown exception (or is not a base class of the type of exception thrown), then the exception is not caught, and the program will terminate. If, however, the type of exception thrown does match that in the catch statement, then the code in the following catch block will be executed to handle the exception.
9.3.1 Essential Tools Module Exception Architecture
The RWxmsg class serves as a base class for the various exception types that can be thrown and caught within the Essential Tools Module library, or from within Essential Tools Module client applications.
The Essential Tools Module uses the following hierarchy.
Figure 9 – RWxmsg class hierarchy
As shown in Figure 9, RWxmsg serves as a base class for all exception types. From this point, the three major types of exceptions are derived: external errors, internal errors, and stream buffer allocation errors. Let us describe each of these classes in greater detail.
9.3.1.1 RWxmsg
RWxmsg provides a basic interface for exceptions, from which an exception string is always available. This exception string is obtained through the why method, and is represented as a const char*, single-byte character sequence.
9.3.1.2 RWExternalErr
RWExternalErr is derived from RWxmsg, and is used to report errors caused by external sources over which the library has no control. Currently, this class is used to report errors from RWFileManager and RWCollectable during file processing when an invalid file format is encountered.
9.3.1.3 RWStreamErr
RWStreamErr is an external error reported when invalid stream data is encountered. For example, this class is used to report errors from RWeistream when an invalid stream header is encountered.
9.3.1.4 RWFileErr
RWFileErr is a type of external error reported when file IO operations fail. For example, RWBTreeOnDisk makes extensive use of this class to report file read, seek, and write errors.
9.3.1.5 RWxalloc
RWxalloc is a type of RWxmsg that can be used to report buffer allocation errors.
9.3.1.6 RWInternalErr
RWInternalErr is a type of error that is used to report errors that occur within the Essential Tools Module. For example, RWDateTime uses this class to report operations on invalid date/time objects.
9.3.1.7 RWRegexErr
RWRegexErr is used to report regular expression compilation errors from RWTRegex<T>.
9.3.1.8 RWBoundsErr
RWBoundsErr is used to report invalid indexes into buffers. For example RWCString may report this type of error when an invalid index is provided to the assertElement method.
9.3.2 Using the Error Reporting Classes to Handle Errors
The RWxmsg classes may be used in your applications in a number of ways, depending on whether or not your environment enables exceptions.
9.3.2.1 When Exceptions are Supported
The most common use of the error reporting classes is in your try-catch blocks surrounding the code that could throw an exception. For example, consider the following.
 
RWTRegex<char> r;
try
{
r = RWTRegex<char>(“[a-z]*”);
}
catch (RWRegexErr& e)
{
cout << e.why() << endl; }
In this example, the instantiation (and consequent pattern compilation) could result in a pattern compilation error that would be reported through an exception of type RWRegexErr. Therefore, the instantiation is surrounded in a try-catch block that catches exceptions of type RWRegexErr.
9.3.2.2 When Exceptions are Not Supported
Another use of these classes is in setting “call-back” functions in environments in which exceptions are not enabled.
In Windows environments, the default error-handling scheme is to display a Windows message box with the error string, after which the program terminates. In UNIX environments, the error message is written to stderr, and then the program terminates. However, the Essential Tools Module provides a method of setting a user-defined call-back function to be invoked in place of the default error handler whenever such an error occurs.
The function used to set the call-back is named rwSetErrHandler and is declared as follows.
 
rwErrHandler rwexport rwSetErrHandler(rwErrHandler);
The method accepts as an argument a call-back function conforming to the type rwErrHandler. Upon completion, the method returns the previously installed error handler. Call-back functions must have the following signature, as defined by the rwErrHandler typedef.
 
typedef void (*rwErrHandler)(const RWxmsg&);
The function must return void and accept a const reference to an RWxmsg object.
Example:
 
#include <iostream>
#include <rw/compiler.h>
#include <rw/rwerr.h>
#include <rw/coreerr.h>
 
#ifdef RW_NO_EXCEPTIONS
 
void myOwnErrorHandler(const RWxmsg& error)
{
std::cout << "myOwnErrorHandler(" << error.why() << ")" << std::endl;
}
 
int main()
{
// Comment out the following line to get the default error handler.
rwSetErrHandler(myOwnErrorHandler);
RWTHROW( RWExternalErr("Error!"));
std::cout << "Done." << std::endl;
 
return 0;
}
 
#else // RW_NO_EXCEPTIONS
 
#include <stdio.h>
 
int main ()
{
printf ("This example is only for C++ "
"compilers with no exceptions support.\n");
}
 
#endif