Site Menu Project Specification Implementation Recommendations Reference Needs Updating Work in Progress Wastebasket Wiki Manual |
Exception Handling Using Dispatch TableSynopsisThis article describes a lightweight exception handling system that is entirely library based and does not require any language support. It employs dispatch tables to store handlers for exceptions. In order to raise an exception, a dispatch table must first be created and a handler for the exception must be stored in the table. Required OperationsThe following operations are required:
All these operations can be implemented without any language support. However, depending on the desired semantics, the raise operation may need to return to the caller's caller. If that is desired, then a primitive in module Example CodeThe following example shows how to install custom exception handlers into an imported library module's exception dispatch table: MODULE UseExceptions; IMPORT Foobar; PROCEDURE CustomFooExceptionHandler; BEGIN ... END CustomFooExceptionHandler; PROCEDURE CustomBarExceptionHandler; BEGIN ... END CustomBarExceptionHandler; BEGIN (* Main *) Foobar.exceptions["FooError"] := CustomFooExceptionHandler; Foobar.exceptions["BarError"] := CustomBarExceptionHandler; Foobar.DoStuff; (* does control return here if exceptions occur while executing DoStuff? *) END UseExceptions. The following example shows how to declare and export an exception dispatch table for use by client modules: DEFINITION MODULE Foobar; IMPORT Exceptions; VAR exceptions : Exceptions; PROCEDURE DoStuff; END Foobar. The following example shows how to create and initialise an exception dispatch table and how to raise exceptions: IMPLEMENTATION MODULE Foobar; PROCEDURE DoStuff; BEGIN REPEAT dangerousOperation; IF fooError THEN Exceptions.raise(exceptions, "FooError"); (* or here? *) END; IF barError THEN Exceptions.raise(exceptions, "BarError"); (* and here? *) END; UNTIL endCondition; doMoreStuff; doYetMoreStuff; (* or here? *) RETURN; END DoStuff; PROCEDURE DefaultFooExceptionHandler; BEGIN ... END DefaultFooExceptionHandler; PROCEDURE DefaultBarExceptionHandler; BEGIN ... END DefaultBarExceptionHandler; BEGIN (* module initialisation *) NEW(exceptions); exceptions["FooError"] := DefaultFooExceptionHandler; exceptions["BarError"] := DefaultBarExceptionHandler; END Foobar. Library InterfaceThe following shows the interface for the exception library: DEFINITION MODULE Exceptions; (* Library based Exception Handling *) (* Dispatch Table *) TYPE Exceptions = OPAQUE; (* Exception Type *) TYPE Exception = ARRAY 32 OF CHAR; (* Exceotion Handler Type *) TYPE Handler = PROCEDURE; (* Allocator *) PROCEDURE [NEW] new ( VAR dispatchTable : Exceptions ); (* Allocates and initialises a new dispatch table and passes it back in <dispatchTable>. Returns NIL if the allocation failed. This procedure is bound to pervasive procedure NEW for arguments of type Exceptions. *) (* Accessor *) PROCEDURE [.] handlerForException ( dispatchTable : Exceptions; e : Exception; VAR found : BOOLEAN ) : Handler; (* Searches for exception <e> in <dispatchTable>. If it is found then TRUE is passed back in <found> and the topmost handler of <e> is returned, otherwise FALSE is passed back in <found> and NIL is returned. This function is bound to the array operator [ ] for rvalues of type Exceptions.*) (* Mutators *) PROCEDURE [!] AddHandler ( dispatchTable : Exceptions; e : Exception; handler : Handler ); (* Adds a new exception handler on top of the handler stack for exception <e> in <dispatchTable>. If NIL is passed in for <handler> then the topmost handler for <e> is removed from <dispatchTable>. This procedure is bound to the array operator [ ] for lvalues of type Exceptions. *) (* Membership test *) PROCEDURE [IN] handlerExists ( dispatchTable : Exceptions; e : Exception ) : BOOLEAN; (* Returns TRUE if a handler exists for exception <e> in <dispatchTable>, returns FALSE otherwise. This function is bound to the IN operator for type Exceptions. *) (* Raise an exception *) PROCEDURE Raise ( dispatchTable : Exceptions; e : Exception ); (* Raises exception e by retrieving and calling the topmost handler for <e> in <dispatchTable>. When the handler returns, the procedure will initiate the runtime system's termination sequence to terminate the program. *) (* Resume from exception *) PROCEDURE Resume; (* Returns control to the procedure that raised the current exception. This procedure is used to prevent procedure Raise to initiate termination. *) (* Exception counter *) PROCEDURE [COUNT] exceptionCount ( dispatchTable : Exceptions ) : LONGCARD; (* Returns the number of exceptions defined in <dispatchTable>. This function is bound to pervasive function COUNT for type Exceptions. *) (* Iterator *) PROCEDURE [FOR] nextException ( dispatchTable : Exceptions; VAR e : Exception; VAR handler : Handler ); (* Finds the successor of <e> and passes its exception/handler pair back in <exception> and <handler>. If the null key is passed in then the first exception/handler pair is passed back. If no successor is found then the null key is passed back in <e> and <handler> is undefined. This procedure is bound to the FOR .. IN iterator for type Exceptions. *) (* Destructor *) PROCEDURE [DISPOSE] dispose ( VAR dispatchTable : Exceptions ); (* Deallocates <dispatchTable> and passes NIL back. This procedure is bound to pervasive procedure DISPOSE for arguments of type Exceptions. *) END Exceptions. |