Organization: K.U.Leuven - Academic Computing Center
Date:         Mon, 16 Nov 1992 18:19:25 +01
Comments:     Warning -- original Sender: tag was NETNEWS@CC1.KULEUVEN.AC.BE
From:         Ton Hospel <stud05@CC4.KULEUVEN.AC.BE>
Subject:      Re: (no subject given)

What, something trivial like that as a commercial product ? The fact that
you can use macros to do exception handling in C is well known, see for
example Byte magazine. The code I always use is loosely based on this article.
So if you are interested, here you have it for free without copyrights:

The file except.h:
--------------------------------------------------------
/*
 * A general-purpose exception-handling system for C
 *      based on the Byte article by Jonathan Amsterdam, 1991
 *
 * Heavy modifications by Ton Hospel, 1992
 */

#ifndef __EXCEPTION_H
#define __EXCEPTION_H

#include <setjmp.h>

typedef void    ExceptionCleanup(                 int Args, void **Item);
typedef void    ExceptionAction(const char *Name, int Args, void **Item);

typedef struct {
    const char       *Name;    /* Name of this exception (default errortext) */
    ExceptionCleanup *cleanup; /* Item cleanup code                          */
    ExceptionAction  *action;  /* Code to execute when no handler            */
    void             *Item[4]; /* Saved arguments of Raise()                 */
    int               Args;    /* Number of arguments given to public Raise()*/
#ifndef   SIMPLE_EXCEPTIONS
    const char       *File;    /* Name of file containing the Raise()        */
    int               Line;    /* Linenumber of Raise() in this file         */
#endif /* SIMPLE_EXCEPTIONS */
}               Exception;

typedef struct jbr {
    jmp_buf         jb;
    struct jbr     *Next, *Self;
} JmpBufRec;

extern ExceptionAction DefaultExceptionAction, ErrnoExceptionAction;
extern const char *ExceptionProgram;
/*      Variable containing the current exception       */
extern Exception *theException;

/*      Some predefined exceptions                      */
extern Exception OutOfMemory, ReRaiseException,
                 FatalException, ErrnoFatalException;

extern void     CleanHandlers(void);
extern int      TestHandlers(const char *Context);
extern void     PushJbr(JmpBufRec *jbr);
extern void     PopJbr(void);
/* The real exception raiser */
extern void     _raise_(Exception * exception);

#define ExceptionP(Exception)   (theException == &(Exception))
#define ExceptionName()         (theException->Name)
#define ClearException()        (theException = 0)
#define ExceptionArg(nr, type)  (*(type *) &theException->Item[nr])
#define ExceptionArgs           theException->Args

/* If you use arguments, make sure they are still in scope by the time the */
/* exceptionaction gets called                                             */

#ifndef   SIMPLE_EXCEPTIONS
# define Raise(Exception)                                                    \
do {                                                                         \
        (Exception).Args = 0;                                                \
        (Exception).File = __FILE__;                                         \
        (Exception).Line = __LINE__;                                         \
        _raise_(&(Exception));                                               \
} while (0)

# define Raise1(Exception, Arg0)                                             \
do {                                                                         \
        (Exception).Args = 1;                                                \
        (Exception).File = __FILE__;                                         \
        (Exception).Line = __LINE__;                                         \
        (Exception).Item[0] = (void *) (Arg0);                               \
        _raise_(&(Exception));                                               \
} while(0)

# define Raise2(Exception, Arg0, Arg1)                                       \
do {                                                                         \
        (Exception).Args = 2;                                                \
        (Exception).File = __FILE__;                                         \
        (Exception).Line = __LINE__;                                         \
        (Exception).Item[0] = (void *) (Arg0);                               \
        (Exception).Item[1] = (void *) (Arg1);                               \
        _raise_(&(Exception));                                               \
} while (0)

# define Raise3(Exception, Arg0, Arg1, Arg2)                                 \
do {                                                                         \
        (Exception).Args = 3;                                                \
        (Exception).File = __FILE__;                                         \
        (Exception).Line = __LINE__;                                         \
        (Exception).Item[0] = (void *) (Arg0);                               \
        (Exception).Item[1] = (void *) (Arg1);                               \
        (Exception).Item[2] = (void *) (Arg2);                               \
        _raise_(&(Exception));                                               \
} while (0)

# define Raise4(Exception, Arg0, Arg1, Arg2, Arg3)                           \
do {                                                                         \
        (Exception).Args = 4;                                                \
        (Exception).File = __FILE__;                                         \
        (Exception).Line = __LINE__;                                         \
        (Exception).Item[0] = (void *) (Arg0);                               \
        (Exception).Item[1] = (void *) (Arg1);                               \
        (Exception).Item[2] = (void *) (Arg2);                               \
        (Exception).Item[3] = (void *) (Arg3);                               \
        _raise_(&(Exception));                                               \
} while(0)
#else  /* SIMPLE_EXCEPTIONS */
# define Raise(Exception)                                                    \
do {                                                                         \
        (Exception).Args = 0;                                                \
        _raise_(&(Exception));                                               \
} while (0)

# define Raise1(Exception, Arg0)                                             \
do {                                                                         \
        (Exception).Args = 1;                                                \
        (Exception).Item[0] = (void *) (Arg0);                               \
        _raise_(&(Exception));                                               \
} while(0)

# define Raise2(Exception, Arg0, Arg1)                                       \
do {                                                                         \
        (Exception).Args = 2;                                                \
        (Exception).Item[0] = (void *) (Arg0);                               \
        (Exception).Item[1] = (void *) (Arg1);                               \
        _raise_(&(Exception));                                               \
} while (0)

# define Raise3(Exception, Arg0, Arg1, Arg2)                                 \
do {                                                                         \
        (Exception).Args = 3;                                                \
        (Exception).Item[0] = (void *) (Arg0);                               \
        (Exception).Item[1] = (void *) (Arg1);                               \
        (Exception).Item[2] = (void *) (Arg2);                               \
        _raise_(&(Exception));                                               \
} while (0)

# define Raise4(Exception, Arg0, Arg1, Arg2, Arg3)                           \
do {                                                                         \
        (Exception).Args = 4;                                                \
        (Exception).Item[0] = (void *) (Arg0);                               \
        (Exception).Item[1] = (void *) (Arg1);                               \
        (Exception).Item[2] = (void *) (Arg2);                               \
        (Exception).Item[3] = (void *) (Arg3);                               \
        _raise_(&(Exception));                                               \
} while(0)
#endif /* SIMPLE_EXCEPTIONS */

#define ActionException(Exception)                                           \
    if ((Exception)->action)                                                 \
        (*(Exception)->action)((Exception)->Name, (Exception)->Args,         \
                               (Exception)->Item);                           \
    else DefaultExceptionAction((Exception)->Name, (Exception)->Args,        \
                                (Exception)->Item)

#define CleanupException(Exception)                                          \
do {                                                                         \
    if ((Exception)->cleanup)                                                \
        (*(Exception)->cleanup)((Exception)->Args, (Exception)->Item);       \
} while (0)


/*      Reraise the current exception                   */
#define ReRaise()                                                       \
        if (theException) _raise_(theException);                        \
        else              _raise_(&ReRaiseException)

/* So you don't use the raise() from <signal.h> by accident !!! */
/* notice that an exception (lower case first letter) structure */
/* exists in some compilers in math.h                           */

extern int      raise(int sig);

/* Start a piece of code in which you want exception handling       */
/* Code that executes the WITH_... must also execute the            */
/* corrsponding END_... (and they must be properly nested), so e.g. */
/* don't return from inside an exception level                      */
/* Also notice that registers can be saved and restored by setjmp   */
/* and longjmp, so throwing an exception can restore variables      */
/* that have been allocated to a register by the compiler (use      */
/* volatile variables in an ANSI C compiler if you don't want this) */

#ifndef   __STDC__
# define WITH_HANDLING {                                                \
      JmpBufRec jbr;                                                    \
                                                                        \
      PushJbr(&jbr);                                                    \
      if (setjmp(jbr.jb) == 0) {
#else  /* __STDC__ */
# define EXCAT(x, y) x##y
# define CAT(x, y) EXCAT(x, y)
# define WITH_HANDLING {                                                \
      JmpBufRec CAT(jbr,__LINE__);                                      \
                                                                        \
      PushJbr(&CAT(jbr,__LINE__));                                      \
      if (setjmp(CAT(jbr,__LINE__).jb) == 0) {
#endif /* __STDC__ */

/*      In case of exception, start executing the following stuff       */
#define ON_EXCEPTION                                                    \
         PopJbr();                                                      \
      } else {

/*      Ends the part with exception handling                           */
#define END_HANDLING                                                    \
         ClearException();                                              \
      }                                                                 \
   }

/*      Start a piece of code whose cleanup must be executed            */
#define WITH_UNWIND {                                                   \
   WITH_HANDLING

/*      Introduces the cleanup code                                     */
#define ON_UNWIND                                                       \
         PopJbr();                                                      \
      }                                                                 \
   }

/*      Ends the part with unwind handling                              */
#define END_UNWIND                                                      \
   if (theException) _raise_(theException);                             \
}
#endif  /* __EXCEPTION_H */
--------------------------------------------------------
The file except.c:
--------------------------------------------------------
/*
 * A general-purpose exception-handling system for C
 *      based on the Byte article by Jonathan Amsterdam, 1991
 *
 * Heavy modifications by Ton Hospel, 1992
 */

#include <stdio.h>
#include <stdlib.h>                    /* for void exit(int) */
#include <string.h>
#include <errno.h>

#ifdef STRERRORPROTO
extern char *strerror(int err);
#endif /* STRERRORPROTO */

#ifdef SYS_ERRLIST
extern char *sys_errlist[];

char *strerror(int nr)
{
    return sys_errlist[nr];
}
#endif /* SYS_ERRLIST */

#include "except.h"

Exception      *theException = 0;

Exception OutOfMemory         = {"Out of memory (buy more ?!)"};
Exception ReRaiseException    = {"Applied ReRaise() without exception"};
Exception FatalException      = {"Fatal,"};
Exception ErrnoFatalException = {"Fatal,", 0, ErrnoExceptionAction};

/* Stores the name of the program causing the exception, 0 means unknown */
const char *ExceptionProgram = 0;

JmpBufRec      *CurRec = NULL;

void CleanHandlers(void)
{
    CurRec = 0;
}

int TestHandlers(const char *Context)
{
    JmpBufRec *Here, *Next;

    for (Here = CurRec; Here; Here = Next) {
        Next = Here->Next;
        fflush(stderr);
        if (Here->Self != Here || (Next && Here >= Next)) {
            fprintf(stderr, "%s: Corrupted exception stack !!!!!\n", Context);
            fflush(stderr);
            return 1;
        }
    }
    return 0;
}

void            PushJbr(JmpBufRec * jbr)
{
    jbr->Next = CurRec;
    jbr->Self = jbr;
    CurRec = jbr;
}

void            PopJbr(void)
{
    if (CurRec)
        CurRec = CurRec->Next;
    else {
        fflush(stdout);
        fputs("Attemp to pop empty exception stack\n", stderr);
        fflush(stderr);
        exit(1);
    }
}

void            DefaultExceptionAction(const char *Name, int Args, void **Data)
{
    int             i;

    fflush(stdout);
    if (ExceptionProgram) {
        fputs(ExceptionProgram, stderr);
        fputs(": ", stderr);
    }
    fputs(Name, stderr);
    for (i = 0; i < Args; i++) {
        fputc(' ', stderr);
        fputs(Data[i], stderr);
    }
    fputc('\n', stderr);
}

void            ErrnoExceptionAction(const char *Name, int Args, void **Data)
{
    int             i, oldnum;
    const char     *message;

    message = strerror(errno);
    oldnum  = errno;
    fflush(stdout);
    if (ExceptionProgram) {
        fputs(ExceptionProgram, stderr);
        fputs(": ", stderr);
    }
    fputs(Name, stderr);
    for (i = 0; i < Args; i++) {
        fputc(' ', stderr);
        fputs(Data[i], stderr);
    }
    fputs(": ", stderr);
    fputs(message, stderr);
    fputc('\n', stderr);
}

void            _raise_(Exception *exception)
{
    JmpBufRec      *jbr;
    int             nr;

    if (CurRec) {
        theException = exception;
        jbr = CurRec;
        if (jbr->Self != jbr) {
            fflush(stdout);
            fputs("Corrupted exception stack\n", stderr);
            fflush(stderr);
            exit(3);
        }
        PopJbr();
        longjmp(jbr->jb, 1);
    } else {
        if (exception != &FatalException &&
            exception != &ErrnoFatalException) {
            nr = errno;
            fflush(stdout);
#ifdef    SIMPLE_EXCEPTIONS
            fputs("Unhandled exception: ", stderr);
#else  /* SIMPLE_EXCEPTIONS */
            fprintf(stderr, "Unhandled exception in file %s, line %d: ",
                    exception->File, exception->Line);
#endif /* SIMPLE_EXCEPTIONS */
            errno = nr;
        }
        ActionException(exception);
        CleanupException(exception);
        fflush(stderr);
        exit(2);
    }
}
--------------------------------------------------------
Comments: Gated by NETNEWS@AUVM.AMERICAN.EDU