C言語で似非Java風味の例外フロー

Javaにはtry〜catch〜finallyの例外フローが存在するけどC言語には無い。でも実現することはできる。こんな感じで使える。

#include <stdio.h>
#include <stdlib.h>

#include "exception.h"         /* 例外フローの定義 */

/* 例外を投げる関数 */
void sub1(EXCEPTION *e) {
  printf("sub1 start\n");      /* 表示される */
  {
    int *t = malloc(sizeof(int));
    if (t == NULL) exit(1);
    *t = 100;
    THROW(*e, t);              /* Javaのthrow()に対応 */
  }
  printf("sub1 end\n");        /* 表示されない */
}

int main() {
  EXCEPTION e;

  TRY_BEGIN(e) {               /* Javaのtryに対応 */
    sub1(&e);
  }
  CATCH(e, int *a) {           /* Javaのcatch()に対応 */
    printf("catch %d\n", *a);  /* 100と表示される */
    free(a);
  }
  FINALLY {                    /* Javaのfinally()に対応 */
    printf("finally\n");       /* 表示される */
  }
  TRY_END                      /* 必ず必要 */

  return 0;
}

コメントのとおりJavaの例外フローと対応付けられる。これらの構文は次のようにsetjmp/longjmpをマクロで隠す形で定義している。

#ifndef __EXCEPTION_H__
#define __EXCEPTION_H__

#include <setjmp.h>

typedef struct EXCEPTION {
  jmp_buf buf;
  void* value;
} EXCEPTION;

#define TRY_BEGIN(_E) { \
                        if ( setjmp(_E.buf) == 0) {
#define CATCH(_E, _V)   } else { \
                          _V = _E.value;
#define FINALLY         } \
                        {
#define TRY_END         } \
                      }
#define THROW(_E, _V) { \
                        (_E).value = _V; \
                        longjmp((_E).buf, 1); \
                      }

#endif

マクロ展開後の例を見ると理解しやすい。

void sub1(EXCEPTION *e) {
  printf("sub1 start\n");      /* 表示される */
  {
    int *t = malloc(sizeof(int));
    if (t == NULL) exit(1);
    *t = 100;
    {                          /* THROW */
      (*e).value = t;          /* THROW */
      longjmp((*e).buf, 1);    /* THROW */
    }                          /* THROW */
  }
  printf("sub1 end\n");        /* 表示されない */
}

int main() {
  EXCEPTION e;

  {                               /* TRY_BEGIN */
    if ( setjmp(e.buf) == 0) {    /* TRY_BEGIN */
      {
        sub1(&e);
      }
    } else {                      /* CATCH */
      int *a = e.value;           /* CATCH */
      {
        printf("catch %d\n", *a);
        free(a);
      }
    }                            /* FINALLY */
    {                            /* FINALLY */
      {
        printf("finally\n");
      }
    }                            /* TRY_END */
  }                              /* TRY_END */

  return 0;
}

CATCH部分とFINALLY部分は省略しても構文として成立する。