The exception library
--- --------- -------
   When writing code in BASIC, we are used to installing error-handlers to
cope with SWI's that generate errors. In C this is less common, mainly
because of the problem of writing nested error-handlers in a modular way.
Nevertheless, there are still advantages to doing so. 

   In RISC O S, any system call can return an error indicator, of type
|os_error *|, instead of the results that are expected under normal
termination. If the normally-terminating SWI could be considered to be of
type

      Entry -> Exit,

where Entry is the type of its entry conditions and Exit the type of its exit
conditions, then the SWI as provided must be condsidered to be of type

      Entry -> Union (Exit, os_error *).

In general, both Entry asnd Exit are compound types: for example, the type of
pdriver_draw_page() is 

      Struct (int, os_box *, int, char *, int) -> Struct (bool, int)

(which would be rendered into C as void pdriver_draw_page (int, os_box *,
int, char *, bool *, int *). However, this ignores the fact that it might
return an error instead; so the real type is

      Struct (int, os_box *, int, char *, int) ->
            Union (Struct (bool, int), os_error *)

which is rendered as os_error *xpdriver_draw_page (int, os_box *, int,
char *, bool *, int *).

   The problem with using functions of this form is that every single return
value myst be checked, and appripriate action taken if an error has been
returned. This leads to code like

      if ((error = xpdriver_draw_page (1, &req, page, NULL, &more,
            NULL)) != NULL)
         goto finish;

(If you don't like 'goto,' then feel free to imagine there is a call to an
error-handling function there instead. The main argument is not affected by
this.) This has the effect of in effect "wasting" R0 (the register in which
the result is returned), and a following test, on a condition which hardly
ever occurs.

   RISC OS, and the C run-time kernel, provide a mechanism to avoid this. By
calling the non-X form of the SWI, we can ensure that the normal flow of
control of a programme is interrupted when an error occurs, without having to
test for it explicitly. This also means that the return value of the function
can be used to return the most useful output argument. (Which one this is is
a matter of taste.)

   Therefore, the call can be written as

      more = pdriver_draw_page (1, &req, page, NULL, NULL);

To do this requires the installation of an error handler. The C run-time
system does some of this for you. An exception library, the x_ module, does
the rest.

   To use the exception library, code is written in this form:

   {  x_exception x;

      /statements (1)/

      x_TRY (&x)
         /statement (2)/
      x_CATCH (&x)
         /statement (3)/

      /statements (4)/
   }

   The first point is the declaration. To catch an exception locally, the C
run-time system needs some space to store various things. This is provided in
a structure called an |x_exception|. This must be declared like this - on the
stack, not static - or nested error handlers won't work.

   |x_TRY| may be thought of as having a similar syntax to |do|; |x_CATCH()|
to |while|. Both must be present, at the same lexical level, just as above -
if an |x_TRY| is not balanced by an |x_CATCH|, the effects are highly
unpredictable. They must both be provided with pointers to the same
|x_exception| structure. The first statement is usually a block (in {...}),
called the try block, though it could be a single statement. Exceptions
thrown from here cause control to be transferred to the statement after
|x_CATCH()|, called the catch block. If there are no exceptions, control does
not enter the catch block at all. If any exceptions occur in the statement
not in the try block (in the catch block or outside the |x_TRY ... x_CATCH|
construction altogether), they are just thrown as normal (i e, passed up to
enclosing functions).

   After control has passed through |x_TRY()|, |x_RETHROW()| may be used to
propagate the exception further. This would normally be the preferred course.

   This means that within the try block, you are free to put calls to non-x
SWI's. Any SWI error, escape or stack overflow error (any of which can happen
to correct programmes) will be caught and handled locally, without wasting
code on explicit error checking. RISC O S does it for you!

   Normally, any exceptions caught should be rethrown, as in the example code
below, or the code that called you will be unaware of the error that has
occurred. This is an important rule: without it, functions could end up
allocating resources that they would not free, and the whole purpose of the
module would have been subverted. If you think of the whole calling tree at
the moment of the exception, control will pass to the innermost-nested
function that has a catch block; this function should rethrow the exception
to the next enclosing one, and so on until the stack is flat and all
interested functions have had a chance to act.

   The stack does not have to be completely flat, however: normally, a
command-line driven programme would just report the error and ask for another
command, and a WIMP-based one would use Wimp_ReportError and poll again. The
application writer has complete flexibility.

   Blocks written as above may be nested, either in the try block or in the
catch block, and only functions that need to do tidying-up after themselves
need to use |x_TRY| and |x_CATCH|. Functions that they call can just "get on
with the job:" if they call a SWI, or any other function, that raises an
exception, the exception will just "bubble back" through all the error
handlers, back to the level required by the application writer. (For a
command-line programme, this would normally be all the way; for a WIMP
application, it might well be back to the polling loop).

   There are other utilities to round out the module: |x_THROW_LAST_ERROR|
throws the last error detected by the run-time system. It is normally used
after a library call fails. |x_THROW| throws a given error, in just the same
way that the SWI's do, and x_STATIC_ERROR declares one for such use. For
example,

      if ((file = fopen ("DataFile", "w")) == NULL)
         x_THROW_LAST_ERROR ();

      /at this point, we know that |file| is a valid handle/

Code to open a file, write to it and close it again would look like this:

      FILE *f;

      if ((f = fopen ("DataFile", "w")) == NULL)
         x_THROW_LAST_ERROR ();

      x_TRY (&x)
      {  if (fprintf (f, "Hello, World!\n") < 0)
            x_THROW_LAST_ERROR ();

         /lots of complicated output to a file, which might go wrong/
      }
      x_CATCH (&x)
         ;

      if (fclose (f) == NULL)
         x_THROW_LAST_ERROR ();
      x_RETHROW (&x);

Often, the catch block is empty, since the tidying-up code is the same in the
error case as in the correct case.

   There are also functions |x_ALLOC|, |x_FREE| and |x_REALLOC| which are
like |malloc|, |free| and |realloc|, but throw an exception on failure.

Conclusion
----------
   Any SWI can in principal generate an error at any time, for any reason.
These errors are usually associated with the exhaustion of some resource
(memory, file handles, disc space, etc), and writing code that checks
error-returning SWI's, and in turn returns the error pointer back to its
caller, is time-consuming and inefficient. Using a handler, and error-
generating SWI's, saves 1 or 2 instructions per SWI veneer (the veneer
doesn't have the 'BVS %99; MOV R, #0' or 'MOVVC R, #0'); and typically 2 at
each function call ('CMP R0, #0; BNE R0, finish' in the example above). It
also enables more readable code to be produced. For example, if you check all
return codes, a printing loop might look like this:

      if ((error = xpdriver_draw_page (1, &req, page, NULL, &more,
            NULL)) != NULL)
         goto finish;

      while (more)
      {  if ((error = xcolourtrans_set_gcol (os_COLOUR_BLACK,
               colourtrans_SET_FG_GCOL, os_ACTION_OVERWRITE, NULL, NULL))
               != NULL)
            goto finish;

         if ((error = xfont_paint (0, t, NONE, 0, 0, NULL, NULL, 0))
               != NULL)
            goto finish;

         if ((error = xpdriver_get_rectangle (&req, &more, NULL)) !=
               NULL)
            goto finish;
      }

   finish:

whereas if you can call x-clear SWI's, you can write instead:

      for (more = pdriver_draw_page (1, &req, page, NULL, NULL);
            more; more = pdriver_get_rectangle (&req, NULL))
      {  colourtrans_set_gcol (os_COLOUR_BLACK, colourtrans_SET_FG_GCOL,
               os_ACTION_OVERWRITE, NULL, NULL);

         font_paint (0, t, NONE, 0, 0, NULL, NULL, 0);
      }

The first example above compiles to 52 instructions; the second to 44, for a
saving of an enormous 20% in code size (and knock-on effects for callers).
This could also be expected to translate into a 20% speed-up as well, since
all the extra instructions have to be executed. The code is also
substantially easier to write, understand and modify.

   The x_ module does not interfere in any way with errors that are not
expected: namely, abort, arithmetic exception, illegal instruction, bad
memory access, termination request. These should probably only be handled at
the very top level in order to provide a last-ditch error handler: it could
do something like saving all unsaved data into a scrap file.

   The C library throws an escape condition when the user presses Escape, but
this behaviour is normally suppressed by the WIMP, except in special cases
like printing. By enabling and disabling escapes around Wimp_Poll, an
application could be easily written to abort its current operation on Escape
and return to its main polling loop, with very little special code needed,
and no risk of files being left open, etc.

Other issues
----- ------
   At present, modules written in C are compiled without stack-checking, and
any SWI called from an application will /generate/ an error on stack
overflow, rather than /returning/ one, even if it was called with the X bit
set. If the module were to be compiled with stack-checking enabled, it could
catch stack overflow errors and return a stack-overflow error to the
application. This would mean more graceful degradation in low-memory
situations.

   It might also be useful to have an interface to the register set at the
point of the exception, so that handlers could display a register dump and
backtrace, for support purposes. This would require a small
modification to the C library.

   The OSLib SWI veneers provide both X and non-X forms of each SWI
function. When writing an application, or a library to be used by others,
there is a big decision to make: do its functions return |os_error *|, and
call X-form SWI's, or do they return a useful result and call non-X-form
SWI's (with error-handlers in the right places)? In other words, do the
library functions mimic x-set SWI's, or x-clear SWI's?

   The libraries and modules described enable us to write better applications
in less time, that run faster and require less memory to run in. 
