From: crj10@phx.cam.ac.uk (Clive Jones)
Subject: Re: Exception handling in C.
Telephone-Messages: +44 223 33 4900
Address: Christ's College, Cambridge CB2 3BU, ENGLAND.
Organization: 6am Digital Playground
X-Furry-Character: Gerald_Duck
Date: Wed, 11 Nov 1992 22:38:24 GMT

In article <1992Nov9.135023.12048@waikato.ac.nz>, bwc@waikato.ac.nz (Ug!) writes:
> I'd like to discuss a few things about what I am implementing at the moment,
> first among them being the issue of exception handling in C.

I will have exception handling available Real Soon Now in Desktop C. I intend
to release it into the public domain, as part of a package of extra 'primitives'
to alter the flow of control in C programs (or, I suppose, Pascal, or other
APCS languages).

The complete package will contain:
  Coroutines
  Vectors
  Exceptions
  (and possibly) Timer-activated pre-emptive triggering of functions.
The combination of coroutines and pre-emptive function calls obviously combine
to provide pre-emptive multitasking WITHIN a C program.

> Exception handling is always a messy business, and C doesn't cope
> particularly well with it, although in fact I've yet to meet a language
> which does.

C has *no* explicit mechanism for passing exceptions around in your programs -
it only provides the signal and errno interfaces to the standard library and
operating system. A cynic might say that even those are only there to aid
portability, and not for the tidier programming style they engender. (-8

Exceptions are handled well, IMO, by Modula-3, PL/1 and BBC Basic 4. Admittedly,
the LOCAL/RESTORE ERROR pair of commands are primitive, but they do the
business. Look at the other two languages as well if you want some ideas for
nice exception systems.

[...] 
> *  exceptions should be interceptable.  For instance, if I call a routine
>    to open a file, and the routine raises an exception if that file doesn't
>    exist, then an error message may not always be appropriate for that
>    situation.  I should be able to intercept that exception, and in this
>    case, decide not to raise an error.

In other words, you should be able to locally handle exceptions, as well as
having the global handlers. This is, to my mind, the vital difference between
a proper exceptions mechanism and a simple signal system.
 
> *  exceptions should not alter the normal flow of code.  If you allow
>    an exception to land you somewhere else in the program, then you end
>    up with a mess that makes spaghetti look friendly.  Instead, exceptions
>    should be coped with in a manner resembling interrupt handlers.

The two halves of that seem to contradict. I agree with the first half, which
basically means that an exception shouldn't let you go somewhere you couldn't
go with a longjmp(), for example. However, this seems to contradict the idea of
it behaving like an interrupt - i.e. calling some function when an exception
occurs, thus breaking the normal flow of control.

> Those were my base ideas for handling exceptions.  What I came up with was
> essentially each module registering a handler with an 'error' module.  The
> most recent registered handler is passed the exception first.  If it doesn't
> handle the exception, then the next most recent handler is called, and so on.

Fair enough, for some uses...

> How do other people cope with exceptions?  Is there something I'm missing?
> I've noticed that my way of doing adds the additional ability to cope well
> with internationalisation, which is an added bonus.

The structure I decided upon was as follows:

  1) Each module can produce some anomalous circumstances. Classify these, and
     you ought to have "exception classes". Try and arrange it so that each of
     these exceptions can be expressed by a pointer to a struct, or similar.

  2) For each of these, provide an extern function pointer to a handler. By
     default, have this pointing at something which just generates a textual
     error message, and passes it to an error handler function (which is just
     another extern function pointer, but one which is always fed a char *).
     The default function on this 'hook' should print the message to stderr and
     exit.

  3) NOW - each module is standalone, and doesn't rely on any given exception
     mechanism. This means that in simple programs, you can forgo ANY exception
     system and save space. It also means that someone else can come along with
     their own pet exception handling system and bolt it on instead. However,
     then also supply...

  4) An exceptions mechanism. Provide a standard function exception_raise(),
     for example, which can be set as the handler function for all the
     anomalous situations in any appropriate modules. Also provide an
     exception_handle() function, akin to setjmp(), which returns NULL when
     called directly, but a pointer to an exception if one is returned via
     it. Bind this up in some 'syntactic sugar' such as:
       #define try { if (exception_handle())
       #define except else { switch (exception_type())
       #define endtry exception_passon(); } exception_unhandle(); }
     ...so that you can catch exceptions in a tidy structure of the form:
       try
        {
         f = openfile("Spong", "r");
        }
       except
        {
         case ex_FILENOTFOUND:
           ...
        }
       endtry

     (Try substituting the macros above to see roughly how that would work!)

     Your exception_raise() function should then backtrace the stack until it
     finds an exception handler, and then call it. If it hits the end of the
     program, it should call some default function. You can use longjmp if
     you're not brave enough to write some assembler to do this.

  5) For added spice, you could actually attach a vector to each of the
     anomalous condition handlers instead, and put the exception mechanism as
     one of the claimants of the vector. You could then do cunning things like
     report the exception to a different coroutine if the generating
     coroutine didn't handle it, etc. - you get the idea.

Hope this is of *some* interest,         

--Clive.