Exceptions in C

Introduction

I'm a fairly experienced C programmer, but just realized something for the first time today which I find very cool (and useful): one can implement exception handling in ANSI C. I'll show in detail here how it's done.

Background

The whole thing comes down to two standard ANSI functions: setjmp and longjmp . Their prototypes are:

#include <setjmp.h>

int setjmp(jmp_buf jb);
void longjmp(jmp_buf jb, int val);

Now here's how they work. You call setjmp at some point in your code on the jmp_buf jb, for instance; it returns zero. Later on, at any point, you can call longjmp(jb, val), for some integer val. This, in effect, returns execution back to the setjmp function which this time returns val. For example, consider the following code segment:

jmp_buf jb;

void f2() {
  longjmp(jb, 1);
}

void f1() {
  if (!setjmp(jb)) {
    printf("first time\n");
    f2();
  } else {
    printf("second time\n");
  }
}

Let's trace through a call to f1. First, setjmp is called on jb. The return value is zero. The message "first time" is printed on the screen and f2 is called. The function f2 immediately calls longjmp. This returns execution back to the line with the setjmp, and has the same effect as that setjmp returning with return value 1. Thus the else block is executed and the message "second time" is printed.

Implementing Exceptions

Once you have setjmp and longjmp, implementing exceptions becomes easy. I've done it like this: I declare two global variables:

jmp_buf __exbuf;
int     __exvalue;
and then define macros
#define TRY       __exvalue=setjmp(__exbuf);       \
                  if (__exvalue==0) { __pushtry();
#define CATCH(x)  __poptry(); } else {             \
                  struct __ex_t *x;                \
                  (x)=(struct __ex_t *)__exvalue;  \
                  __poptry();
#define ENDTRY    }

(here __ex_t is just some structure which has information about the exception). The functions __pushtry and __poptry explain themselves. To throw an exception, just do a longjmp to the jmp_buf on the top of the stack.

Assuming we have functions xfopen and xfclose which work like fopen and fclose except they generate exceptions on errors, we could use the above structure as follows:

FILE *inf;

TRY
  inf=xfopen("test.txt", "rt");
  printf("file opened successfully\n");
  xfclose(inf);
CATCH(ex)
  print_ex_info(ex);
ENDTRY

If you prefer, you can enclose your TRY and CATCH block in braces; it doesn't really matter though.


July 7th, 2004