Error handling according to the GNU libg++-2.6 C++ class library. ---------------------------------------------------------------------- * All classes use the same simple error (exception) handling strategy. Almost every class has a member function named `error(char* msg)' that invokes an associated error handler function via a pointer to that function, so that the error handling function may be reset by programmers. By default nearly all call `*lib_error_handler', which prints the message and then aborts execution. This system is subject to change. In general, errors are assumed to be non-recoverable: Library classes do not include code that allows graceful continuation after exceptions. ---------------------------------------------------------------------- Most GNU C++ library classes possess a method named `OK()', that is useful in helping to verify correct performance of class operations. The `OK()' operations checks the "representation invariant" of a class object. This is a test to check whether the object is in a valid state. In effect, it is a (sometimes partial) verification of the library's promise that (1) class operations always leave objects in valid states, and (2) the class protects itself so that client functions cannot corrupt this state. While no simple validation technique can assure that all operations perform correctly, calls to `OK()' can at least verify that operations do not corrupt representations. For example for `String a, b, c; ... a = b + c;', a call to `a.OK();' will guarantee that `a' is a valid `String', but does not guarantee that it contains the concatenation of `b + c'. However, given that `a' is known to be valid, it is possible to further verify its properties, for example via `a.after(b) == c && a.before(c) == b'. In other words, `OK()' generally checks only those internal representation properties that are otherwise inaccessible to users of the class. Other class operations are often useful for further validation. Failed calls to `OK()' call a class's `error' method if one exists, else directly call `abort'. Failure indicates an implementation error that should be reported. With only rare exceptions, the internal support functions for a class never themselves call `OK()' (although many of the test files in the distribution call `OK()' extensively). Verification of representational invariants can sometimes be very time consuming for complicated data structures. ---------------------------------------------------------------------- #include #ifdef __GNUG__ #define _VOLATILE_VOID volatile void #else #define _VOLATILE_VOID void #endif typedef void (*one_arg_error_handler_t)(const char*); typedef void (*two_arg_error_handler_t)(const char*, const char*); extern _VOLATILE_VOID default_one_arg_error_handler(const char*); extern _VOLATILE_VOID default_two_arg_error_handler(const char*, const char*); extern two_arg_error_handler_t lib_error_handler; extern two_arg_error_handler_t set_lib_error_handler(two_arg_error_handler_t f); ----error.cc--------- #ifdef __GNUG__ #pragma implementation #endif #include #ifdef __GNUC__ typedef _VOLATILE_VOID (*NoReturnFunc)(void); /* Cast abort to a _VOLATILE_VOID function, even if differs. This is to avoid a warning from g++ that a `volatile' function does return. */ #define ABORT() ((NoReturnFunc)abort)() #else #define ABORT abort() #endif _VOLATILE_VOID default_one_arg_error_handler(const char* msg) { fputs("Error: ", stderr); fputs(msg, stderr); fputs("\n", stderr); ABORT(); } _VOLATILE_VOID default_two_arg_error_handler(const char* kind, const char* msg) { fputs(kind, stderr); fputs(" Error: ", stderr); fputs(msg, stderr); fputs("\n", stderr); ABORT(); } two_arg_error_handler_t lib_error_handler = default_two_arg_error_handler; two_arg_error_handler_t set_lib_error_handler(two_arg_error_handler_t f) { two_arg_error_handler_t old = lib_error_handler; lib_error_handler = f; return old; } --------------------------- #include void error(const char* msg) const; int OK() const; ---String.cc--------------- void String::error(const char* msg) const { (*lib_error_handler)("String", msg); } int String::OK() const { if (rep == 0 // don't have a rep || rep->len > rep->sz // string oustide bounds || rep->s[rep->len] != 0) // not null-terminated error("invariant failure"); return 1; } int SubString::OK() const { int v = S != (const char*)0; // have a String; v &= S.OK(); // that is legal v &= pos + len >= S.rep->len;// pos and len within bounds if (!v) S.error("SubString invariant failure"); return v; }