|
|
There are certain errors that are difficult or impossible for clients to guard against because they are due to malfunctions of the component itself. We call these internal errors. Actually, such malfunctions are almost always due to problems external to the component, in the layers of software or hardware on which the component is built. When the component is viewed by the client as a black box, however, such errors appear internal to the component, which is why we prefer this term.
The Time(3C++) component furnishes a good example of an internal error. Time(3C++) obtains information about the host machine timezone from the TZ environment variable. This should not normally concern the client, since TZ is set by the system login file on most UNIX systems. If TZ is not set, however, it is an internal error.
Since clients are clearly not responsible for internal errors, it would be irresponsible for components not to inform them when such errors occur. Such negligence would lead to program failures of the worst kind: ones for which no amount of tracing or debugging on the part of the client programmer would reveal the cause! Instead, components check for internal errors and inform clients via a mechanism defined in the Objection(3C++) component.
The following series of examples illustrates how Objections are used to handle internal errors in the Time component. It is by no means a complete discussion of Objections - for that, see An Objection Class for Rudimentary Error Handling.
First consider a simple Time client:
#include <Time.h> main() { Time t; }If this program is executed in a shell that does not have the TZ variable set, the program will be terminated and the message:
TZ environment variable not set!will be written to the standard error file. This is probably what 99% of all programmers would want to happen anyway.
If the client wants the program to continue functioning despite the internal error, it may request that the Time component ignore the internal error. Class Time has a static public data member of type Objection called environment_objection. The constructor invokes the raise() operation on this object when it detects the internal error. By invoking the ignore() operation on the same object, the client requests the effect described below:
#include <Time.h> main() { Time::environment_objection.ignore(); Time t; }This suppresses the message and causes default timezone parameters to be used for the local timezone. The latter is called a recovery action, but this term is probably a misnomer since there is no universally appropriate action to remedy an internal error (if there were, internal errors would not be an issue). In truth, a recovery action is something arbitrary, yet well-defined, that allows the program to continue running.
Alternatively, the programmer can write a function that makes a serious attempt to remedy the internal error:
#include <Time.h> #include <stdlib.h> int tz_handler(const char*) { putenv("TZ=PST8PDT"); // see stdlib.h return 1; } main() { Time::environment_objection.appoint(tz_handler); Time t; }This is called handling an Objection. The handler sets the TZ environment variable with a value that has a better chance of being the correct than the default one defined in the recovery action. It reflects where the client programmer thinks his application might be running, rather than, say, the location where the Time component was written.
Objections are an interim solution, intended to bridge the gap until C++ Standard Components are modified to use exceptions. Because exceptions cannot be simulated using function call and return, the advent of exceptions will necessitate source-incompatible changes to C++ Standard Components. However since Objections are used very sparingly, conversion should be easy for most users.