DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
No more ctime(S) errors - Time(C++)

Objections

There are certain error conditions that are difficult or impossible for the client to guard against. For example, a client that extracts a string from the standard input can not easily guarantee that the string represents a valid time-of-day before passing it to make_duration(). It would therefore be unreasonable for make_duration() to require this as a precondition. Instead, the functions themselves do the checking and raise Objections when the conditions are not satisfied.

Objections support several error handling paradigms. If the client does nothing, the default action (abort with an error message) will be taken when an Objection is raised. Alternatively, the client can write a function to handle a particular Objection; the handler will be called whenever the associated Objection is raised, allowing the client to take any desired action. Or, the client may specify that certain kinds of errors be ignored, causing a ``recovery'' action to be performed whenever the corresponding Objection is raised.

Example 1: handling Duration::string_objection

Consider a program that solicits input from the user and converts it to a Duration:

       #include <Time.h>
       #include <iostream.h>
       #include <String.h>
       main(){
           Duration d;
           String s;
           cout << "enter the time-of-day: ";
           cin >> s;
           d = make_duration(s);
           do something with d    }

This works unless the user inputs a bad string; make_string() will raise Duration::string_objection, causing the client program to abort with the (not very user-friendly) message:

       Duration::string_objection raised
       Abort(coredump)

By handling the objection, we can achieve a more user-friendly interaction:

       #include <Time.h>
       #include <iostream.h>
       #include <String.h>
       int error;
       int string_objection_handler(const char*){
           cerr << "\ nbad string; please try again\ n";
           error=1;
           return 1; // recover
       }
       main(){
           Duration d;
           String s;
           do{
               error = 0;
               cout << "enter the time-of-day: ";
               cin >> s;
               d = make_duration(s);
           }while(!error);
           do something with d    }

The handler displays a helpful message (the default message is passed to, but ignored by, the handler) and sets a global flag which causes the client program to loop again, soliciting new input. The return value of 1 requests make_duration() to ``recover'' by computing a harmless result which is subsequently ignored by the client.

Example 2: handling Time::environment_objection

The following program only works if the TZ environment variable is set:

       #include <Time.h>
       main(){
           Time t(1988,Time::january,1);
       }

If TZ is not set, the constructor will raise the Objection Time::environment_objection, causing the client program to abort with the message

       TZ environment variable not set!
       Abort(coredump)

By invoking the ignore() operation on this Objection, the client may request that an unset TZ be ignored; it requests the program to behave as if TZ=GMT0: had been specified:

       #include <Time.h>
       main(){
           Time::environment_objection.ignore();
           Time t(1988,Time::january,1);
           cout << t.make_string("%x %X %Z") << endl;
       }

This prints

       01/01/88 00:00:00 GMT

This is not a very good solution because it is highly unlikely that the program is running in the Greenwich timezone.

Alternatively, we may attempt to handle the Objection by setting the environment variable to a more likely value. To do this, we must appoint a handler:

       int handler(const char*){
           char* tz = "TZ=EST5EDT";
           putenv(tz);
           return 1;
       }
       main(){
           Time::environment_objection.appoint(handler);
           Time t(1988,JANUARY,1);
           cout << "t = " << t.make_string("%x %X %Z")
                << endl;
       }

This prints:

       01/01/88 00:00:00 EST

While this may be somewhat better than ignoring the Objection, it depends on compiled-in knowledge of where the program is running. If the program is shipped out of the Eastern zone, it will give incorrect results.

The order in which things occur is very important in this example. If t had been declared statically,

       int handler(const char*){
           ...
       }
       Time t(1988,JANUARY,1);
       main(){
           Time::environment_objection.appoint(handler);
           cout << "t = " << t.make_string("%x %X %Z")
                << endl;
       }

then t would be constructed before the call to appoint, with the following effect:

       TZ environment variable not set!
       Abort(coredump)

However we can use a trick to guarantee that ignore is called first:

       int handler(const char*){
           ...
       }
       struct dummy{
           dummy(){
               Time::environment_objection.appoint(handler);
           }
       }f;
       Time t(1988,JANUARY,1);
       main(){
           cout << "t = " << t.make_string("%x %X %Z")
                << endl;
       }

Variations on this procedure can be used when the executable is made up of multiple compilation units.


Previous topic: Preconditions

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005