Error Handlers
In addition to inline error checking, the SourcePro DB error model provides error handlers. Each
RWDBStatus object contains an error handler that can be changed by an application. Every time a new error is to be reported, the installed handler is called with the
RWDBStatus instance as an argument.
Error handlers are changed with the
setErrorHandler()method. Every class of SourcePro DB that has an internal status has a
setErrorHandler() method. When objects of the DB Interface Module are produced by other objects, the produced object's status,
including its error handler, is deep copied from the producer, thus not sharing the implementation of
RWDBStatus between producer object and product object; however, propagating the error handler from producer object to product object. Consequently, an application can control error handling at any level it chooses. An error handler installed in the
RWDBManager is propagated to every object of the DB Interface Module in the application; an error handler installed in an
RWDBDatabase is propagated to each object produced by that
RWDBDatabase, and so on. You can also disable the error handler for an object, and for all objects produced by that object, by calling its
setErrorHandler() method with a
NULL argument.
NOTE: Use an object's setErrorHandler() method to install an error handler for the object and for all the objects it produces. Use setErrorHandler (NULL) to disable an object's error handler and the error handlers of all the objects it produces.
Let's have another look at our example.
// Error Handling - Version 2
void simpleHandler(const RWDBStatus& status) {
cout << status.message() << endl;
}
.
.
.
RWDBManager::setErrorHandler(simpleHandler);
.
.
.
RWCString s; float f;
RWDBReader reader = anotherTable.reader();
while (reader()) {
reader >> s >> f; // process s and f
}
This time we've defined a very simple error handler,
simpleHandler, and installed it into the
RWDBManager. Since the
RWDBManager is at the very top of the tree of object producers, our simple error handler is propagated to our
RWDBReader object. If an error occurs, the runtime behavior of versions 1 and 2 would be similar: print a message, exit the loop, and continue.
Now compare this technique with version 1. You can see that by centralizing our error handling we have been able to simplify our code, but we have lost information about the context of the error. Our program output will not identify what was going on when the error occurred. Of course, we could do this:
// Error Handling - Version 3
void specialHandler(const RWDBStatus& status) {
cout << "Error processing anotherTable: "
<< status.message() << endl;
}
.
.
.
RWDBReader reader = anotherTable.reader();
reader.setErrorHandler(specialHandler);
while (reader()) {
RWCString s; float f;
reader >> s >> f; // process s and f
}
The runtime behavior of this version is identical to that of version 1, and the error handling is not inline, so in some sense this is the best of both worlds. On the other hand, maintaining many special purpose error handlers is a tedious task that we don't recommend undertaking. We included this version simply to show how to install an error handler into an individual object.
We will revisit the subject of error handlers in
More About Error Handlers. First, let's turn our attention to yet another error handling technique.