[net.lang.c] Exception Handling? Impossible!

richw@ada-uts.UUCP (08/12/85)

EXCEPTION HANDLING ??!?

The following includes "files" of macros and a simple modification
of the "exit" routine which allows one to do pseudo-exception-handling
in C.  An example of their use is provided in the "test.c" file.

There are problems with these macros (and the modified "exit"),
I admit.  The purpose of this note is to get feedback on possible
solutions to some of the problems.  I'd also like INTELLIGENT comments
about what's going on (please refrain from verborrhea like "How
could you name the macro 'except_when'?!  Name it 'if_exception'!" ).

Problems I can see right off:

1) The raise macro must still use a return statement (which is why
   I've provided raise_return, for those cases where garbage results
   are inappropriate).

2) Arbitrary statements may be executed between the call to the procedure
   that raised an exception and the handler.

3) Unhandled exceptions come to the attention of the user only when
   another "raise" is attempted or when the program terminates.

4) The raise macro includes a return statement since most exceptions
   (at least in programs that I write) are used to signal alternate
   termination conditions of a procedure.  This implies that "local"
   exceptions can't be raised (e.g. to jump out of nested loops).

5) An "exception" is simply a string.  It's "declaration" requires (?)
   some redundancy (i.e. the variable name and the string literal will
   usually look the same).


-- Rich Wagner

------------------------- test.c -------------------------------

#include "exceptions.h"
#include <stdio.h>

Exception overflow = "Overflow";

raiser(b)
int b;
{
	fprintf(stderr, "Raiser called with %d\n", b);
	if (b) raise(overflow);
	fprintf(stderr, "Leaving raiser\n\n");
}

main()
{
	raiser(0);
	    except_when(overflow)
		    fprintf(stderr, "Caught when it wasn't raised\n");
	    end_except;
	raiser(1);
	    except_when(overflow)
		    fprintf(stderr, "Caught\n");
	    end_except;
	raiser(0);
	raiser(1);
	raiser(1);
	fprintf(stderr, "Last line of main\n");
}

------------------------ test output ----------------------------

Raiser called with 0
Leaving raiser

Raiser called with 1
Caught
Raiser called with 0
Leaving raiser

Raiser called with 1
Raiser called with 1
Unhandled exception: Overflow

------------------------ exceptions.h ---------------------------

extern int _xxraised;
extern char *_xxexc;

#define raise(e) { if (_xxraised) exit(1);\
                   _xxexc = e; _xxraised = 1; return; }
#define raise_return(e,r) { if (_xxraised) exit(1);\
                            _xxexc = e; _xxraised = 1 ; return r; }

#define except_when(e) if (_xxraised) {\
			      if (_xxexc == (e)) { _xxraised = 0;

#define when(e) } else if (_xxexc == (e)) { _xxraised = 0;

#define end_except } else exit(1); }

typedef char *Exception;

------------------------ exceptions.c -----------------------------


#include <stdio.h>

int _xxraised = 0;
char *_xxexc;

exit(status)
int status;
{
	if (_xxraised) {
		fprintf(stderr, "Unhandled exception: %s\n", _xxexc);
                _xxexit(status ? status : 1);
        }
	_xxexit(status);

        /*  _xxexit is the old "exit" we all know and love, */
        /*  just renamed.                                   */
}

robert@cheviot.uucp (Robert Stroud) (08/19/85)

Rich Wagner (richw@ada-uts.UUCP) posted some macros for exception handling
and asked for comments and suggestions.

I can recommend an article in Software Practice & Experience by Peter Lee
a few years ago (1983, pp 389-405) which describes a series of macros and
routines which add exception handling in the style of Ada or Clu to C.
Basically they work by hiding a stack of setjmp/longjmp contexts inside
a macro skeleton of

	BEGIN
	   ...
	EXCEPT
	   WHEN(exception1) ...
	   WHEN(exception2) ...
	   OTHERS ...
	END

I can post the code from the article if there is enough interest.

Robert Stroud,
Computing Laboratory,
University of Newcastle upon Tyne.

UUCP ...!ukc!cheviot!robert
ARPA robert%cheviot.newcastle@ucl-cs.ARPA

richw@ada-uts.UUCP (08/22/85)

I'd like to confirm the obvious:  Yes, I'm VERY interested in having
the code from the paper suggested by Robert Stroud posted (having
posted the original note).  As a reminder, that paper is in the 1983
Software Practice & Engineering (pp.389-405), and is by Peter Lee.

I'd also like to mention the following paper -- I received some personal
mail suggesting it:

"An Exception Handler for C"
     by Allman and Been in the Portland (Summer 85) Usenix proceedings

(Note that the librarian where I work is having trouble tracking
this paper down, so...)

-- Rich Wagner

P.S.  Robert Stroud also name-dropped Clu -- is net.lang.c
      actually starting to show a little class ??  :-)

P.P.S.  Probably not.  If it had class, it'd "talk a little"
        about Smalltalk ("show a little cluster ?")

richw@ada-uts.UUCP (08/24/85)

I think I've figured out how to improve upon my first try (the original
note) based on Robert Stroud's mentioning a stack for setjmp/longjmp
environments.  ALL of the problems (except for the fairly trivial #5)
which I mentioned in the first note seem to be taken care of now.
I also fixed a problem not realized originally (i.e. why a stack
is needed instead of a single environment...)

IN ANY CASE, my new test files (test.c and add.c) and the files
which encapsulate the exception handling mechanism (exceptions.{c,h})
follow.  I'm not claiming the mechanism is bug-free!!  I still haven't
read (or found) the papers suggested, so I'm still kinda "wingin'-it"...
I DO know that THIS test case seems to work.

Again, I'd love comments...

-- Rich Wagner

P.S.  Because I use a funny terminal, I can't directly type left
and right-square-brackets.  In the following code, these are
represented by [ and ], respectively.

P.P.S.  Many thanks to Robert Stroud !!

-------------------------------------------------------------------------
/*********  exceptions.h  *********/

#include <setjmp.h>
#include <stdio.h>

typedef char *Exception;

extern jmp_buf _env_stack[];
extern int _env_index, _bad_index;
extern Exception _exc;

#define Begin { if (_env_index == _bad_index) {\
                     fflush(stdout);\
		     fprintf(stderr, "Handlers nested to deeply\n");\
		     exit(1);}\
		if (!setjmp(_env_stack[_env_index++])) {

#define Except      _env_index--;

#define When(e) } else if (_exc == e) {\
		    _env_index--;

#define Others  } else if (1) {\
                    _env_index--;
			
#define End     } else {\
                    fflush(stdout);\
		    fprintf(stderr,"Unhandled Exception: %s\n",_exc);\
		    exit(1);\
		} }

#define raise(e) {_exc = e; longjmp(_env_stack[_env_index-1],1); }

-------------------------------------------------------------------------
/*********  exceptions.c  *********/

#include <setjmp.h>

#define STACK_SIZE 50

jmp_buf _env_stack[STACK_SIZE];
int _env_index = 0;
int _bad_index = STACK_SIZE;
char *_exc;

-------------------------------------------------------------------------
/*********  test.c  *********/

#include <stdio.h>
#include "exceptions.h"

extern Exception overflow;
extern Exception underflow;

main()
{
    int x;

    Begin
	fprintf(stderr, "Calling add(4,1)\n");
	x = add(4,1);
	fprintf(stderr, "After call to add(4,1)\n");
    Except
        When(overflow)
	    fprintf(stderr, "Overflow caught but not expected\n");
    End;

    Begin
	fprintf(stderr, "Calling add(4,7) -- expect overflow\n");
	x = add(4,7);
	fprintf(stderr, "After call to add(4,7)\n");
	fprintf(stderr, "Expected exception not raised");
    Except
        When(overflow)
	    fprintf(stderr, "Overflow caught\n");
    End;

    Begin
	fprintf(stderr, "Calling add(-4,-7) -- expect underflow\n");
	x = add(-4,-7);
	fprintf(stderr, "After call to add(-4,-7)\n");
	fprintf(stderr, "Expected exception not raised");
    Except
        When(overflow)
	    fprintf(stderr, "Overflow caught but not expected\n");
    End;
    
    fprintf(stderr, "Last line of main\n");
}

-------------------------------------------------------------------------
/*********  add.c  *********/

#include "exceptions.h"

Exception overflow  = "Overflow";
Exception underflow = "Underflow";

static my_add(a, b)
int a, b;
{
    a += b;
    if (a >= 10)  raise(overflow);
    if (a <= -10) raise(underflow);
    return (a);
}

add(a, b)
int a, b;
{
    Begin
        return my_add(a,b);
    Except
        When(overflow)
            raise(overflow);
        When(underflow)
            raise(underflow);
    End;
}
	   

richw@ada-uts.UUCP (08/26/85)

Sorry if this is getting boring, but...

I fixed a bug; a "raise" occuring before a "Begin" is encountered
works as one would expect now.  I also made things a little more
space efficient.  Otherwise, the macros' behavior hasn't changed.

Also, note that the environment stack has a fixed depth -- this can
be changed (at the expense of more procedure calls) by having pushed
environments be allocated from the heap (i.e. use malloc).

The updated exceptions.{c,h} follow:

-----------------------  exceptions.h  ----------------------------
#include <setjmp.h>

typedef char *Exception;

extern jmp_buf _env_stack[];
extern int _env_index, _bad_index;
extern Exception _exc;

#define Begin { if (_env_index == _bad_index) _env_overflow();\
		if (!setjmp(_env_stack[_env_index++])) {

#define Except      _env_index--;

#define When(e) } else if (_exc == e) {

#define Others  } else if (1) {
			
#define End     } else _unhandled(); }

#define raise(e) (_exc = e,\
                  (_env_index == 0)\
		       ? _unhandled() :\
		       longjmp(_env_stack[--_env_index],1) )

---------------------------  exceptions.c  ----------------------------
#include <setjmp.h>
#include <stdio.h>

#define BELL '\007'   /*  ASCII control-G  */

#define STACK_SIZE 101

jmp_buf _env_stack[STACK_SIZE];
int _env_index = 0;
int _bad_index = STACK_SIZE;
char *_exc;

_unhandled()
{
    fflush(stdout);
    putc(BELL, stderr);
    fprintf(stderr, "\nUnhandled Exception: %s\n", _exc);
    exit(1);
}

_env_overflow()
{
    fflush(stdout);
    putc(BELL, stderr);
    fprintf(stderr, "\nException handlers nested too deeply");
    fprintf(stderr, "\nMaximum is %d\n", STACK_SIZE - 1);
    exit(1);
}