Modula-2 Reloaded

A Modern Typesafe & Literate Programming Notation

Site Menu

Project

Specification

Implementation

Recommendations

Reference

Needs Updating

Work in Progress

Wastebasket

Wiki Manual

edit SideBar

Exception Handling Using Dispatch Table

Synopsis

This 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 Operations

The following operations are required:

  • create a dispatch table
  • insert a new handler
  • remove a handler
  • raise an exception
  • destroy a dispatch table

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 SYSTEM would be useful to allow the return of control to the caller's caller.

Example Code

The 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 Interface

The 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.