WWC snapshot of http://www.alw.nih.gov/Docs/NIHCL/nihcl_12.html taken on Sat Jun 10 19:13:39 1995

Go to the previous, next section.

Exception--Exception Handling

SYNOPSIS

#include <nihcl/Exception.h>

BASE CLASS

NIHCL

DERIVED CLASSES

None

RELATED CLASSES

None

DESCRIPTION

Classes ExceptionTrap, ExceptionAction, AbortException, RaiseException, and Catch are related to error handling and the function NIHCL::setError(). They were designed as an early experiment in implementing exceptions in C++, and will eventually be replaced by the native C++ exception handling mechanism.

Using the NIHCL exception mechanism is slow and error prone, and is not recommended.

CLASS EXCEPTIONTRAP

Class ExceptionTrap establishes an error handling function for setError to call when an error occurs. This error handling function may return, in which case error processing will proceed normally and either abort the program or raise an exception, or it may abort the program immediately.

An error handling function has type exceptionTrapTy, defined as:

typedef void (*exceptionTrapTy)(unsigned& /* error code */,
int& /* severity */);

For example, an error handling function xtrap() would be defined as follows:

void xtrap(unsigned& error, int& sev)
{
// ...
}

The arguments error and sev are the same as those passed to NIHCL::setError() (see nihclerrs).

Creating an instance of class ExceptionTrap establishes an error handling function for the remainder of the block in which it occurs:

{
    ExceptionTrap x(xtrap);

    // NIHCL::setError() will call xtrap() until x goes out of scope

}

When an instance of class ExceptionTrap is destroyed, it restores any previously established error handlers, so such blocks may be nested.

CLASS EXCEPTIONACTION

Class ExceptionAction is the abstract base class for classes AbortException and RaiseException. The default action taken by setError() is to abort the program; however, setError() can raise an exception as an alternative. Derived classes AbortException and RaiseException control the behavior of setError() on a block-by-block, individual error code basis. Class ExceptionAction holds the number of the error code being controlled and the previous action (ABORT or RAISE).

CLASS ABORTEXCEPTION

Class AbortException causes the occurrence of a specific error to result in program termination. For example:

{
    AbortException x(NIHCL_CLTNEMPTY);

    // NIHCL::setError(NIHCL_CLTNEMPTY, ...) will cause program termination
    // until x goes out of scope

}

When an instance of class AbortException is destroyed, it restores the previous exception action for the error code, so such blocks may be nested.

CLASS RAISEEXCEPTION

Class RaiseException causes the occurrence of a specific error to raise an exception to be handled by BEGINX ... ENDX. For example:

{
    RaiseException x(NIHCL_CLTNEMPTY);

    // NIHCL::setError(NIHCL_CLTNEMPTY, ...) will cause an NIHCL_CLTNEMPTY
    // exception to be raise until x goes out of scope

}

When an instance of class RaiseException is destroyed, it restores the previous exception action for the error code, so such blocks may be nested.

HANDLING EXCEPTIONS

Exceptions are handled by means of the BEGINX ... EXCEPTION ... ENDX construct, for example:

Object* Property::get(
    const Object& ob,   // object with property
    const String& name) // name of property
{
    RaiseException x(NIHCL__KEYNOTFOUND);
    BEGINX
        return ((Dictionary*)prop.atKey(ob))->atKey(name);
    EXCEPTION
        case NIHCL__KEYNOTFOUND: return Object::nil;
        default: RAISE(EXCEPTION_CODE);
    ENDX
}

The RaiseException declaration causes KEYNOTFOUND errors to raise an exception rather than causing program termination. Calling the preprocessor macro RAISE with the error code as an argument raises an exception.

Statements between the BEGINX and EXCEPTION macro calls may cause an exception to be raised, in which case control is tranferred to the statements between the EXCEPTION and ENDX macro calls. These statements form the body of a switch statement that tests the value of the error code causing the exception. In this example, KEYNOTFOUND exceptions cause the function to return the pointer to the Nil object.

Control normally passes to the statements following the ENDX whether or not an exception occurs, and whether or not an exception is handled. However, an exception handler can itself raise an exception, causing control to transfer to an enclosing EXCEPTION ... ENDX construct. In this example, the default case of the switch body causes exceptions other than KEYNOTFOUND to be propagated to an enclosing exception handler by re-raising the current exception with the statement RAISE(EXCEPTION_CODE).

CLASS CATCH

When an exception is raised, longjmp() is used to transfer control to the most recently established exception handler, causing class instances that go out of scope as a result to be destroyed without their destructors being called. This must be avoided since it violates the C++ guarantee to call these destructors.

Class Catch was devised in an attempt to solve this problem. The programmer is responsible for including an instance of class Catch as a member variable of any auto class instance that may go out of scope as the result of an exception, and for defining the virtual function destroyer() to finalize class instances just as the class's destructor would.

The constructor for class Catch takes the address of the instance it is a member of (the this pointer) as its argument, and threads the instance on a linked list. When an exception occurs, the destroyer() function is applied to the instances on this list that will go out of scope as a result of the longjmp() to the exception handler.

The destructor for class Catch removes the instance from the linked list and applies destroyer() to it.

This solution is too difficult to use correctly, and the overhead of maintaining the linked list of auto class instances is too great to make this practical.

EXCEPTIONS RAISED

NIHCL_BADERRNUM

Go to the previous, next section.