Skip to content

Instantly share code, notes, and snippets.

@7etsuo
Created October 26, 2025 08:28
Show Gist options
  • Select an option

  • Save 7etsuo/2a93320e011c12ee22683e7874c7b681 to your computer and use it in GitHub Desktop.

Select an option

Save 7etsuo/2a93320e011c12ee22683e7874c7b681 to your computer and use it in GitHub Desktop.
custom exception handler in C using setjmp/longjmp
//// Except.h
#ifndef EXCEPT_INCLUDED
#define EXCEPT_INCLUDED
#include <setjmp.h>
#define T Except_T
typedef struct T { const char *reason; } T;
typedef struct Except_Frame Except_Frame;
struct Except_Frame
{
Except_Frame *prev;
jmp_buf env;
const char *file;
int line;
const T *exception;
};
enum
{
Except_entered = 0,
Except_raised,
Except_handled,
Except_finalized
};
#ifdef _WIN32
extern __declspec (thread) Except_Frame *Except_stack;
#else
extern __thread Except_Frame *Except_stack;
#endif
extern const Except_T Assert_Failed;
void Except_raise (const T *e, const char *file, int line);
#define RAISE(e) Except_raise (&(e), __FILE__, __LINE__)
#define RERAISE \
Except_raise (Except_frame.exception, Except_frame.file, Except_frame.line)
#define RETURN \
switch (Except_stack = Except_stack->prev, 0) \
default: \
return
#define TRY \
do \
{ \
volatile int Except_flag; \
Except_Frame Except_frame; \
Except_frame.prev = Except_stack; \
Except_frame.file = NULL; \
Except_frame.line = 0; \
Except_frame.exception = NULL; \
Except_stack = &Except_frame; \
Except_flag = setjmp (Except_frame.env); \
if (Except_flag == Except_entered) \
{
#define EXCEPT(e) \
if (Except_flag == Except_entered) \
Except_stack = Except_stack->prev; \
} \
else if (Except_frame.exception == &(e)) \
{ \
Except_flag = Except_handled;
#define ELSE \
if (Except_flag == Except_entered) \
Except_stack = Except_stack->prev; \
} \
else \
{ \
Except_flag = Except_handled;
#define FINALLY \
if (Except_flag == Except_entered) \
Except_stack = Except_stack->prev; \
} \
{ \
if (Except_flag == Except_entered) \
Except_flag = Except_finalized;
#define END_TRY \
if (Except_flag == Except_entered) \
Except_stack = Except_stack->prev; \
} \
if (Except_flag == Except_raised) \
RERAISE; \
} \
while (0)
#undef T
#endif
//// Except.c
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Except.h"
#define T Except_T
/* Use thread-local storage for exception stack
* Each thread has its own exception stack - no synchronization needed */
#ifdef _WIN32
__declspec(thread) Except_Frame *Except_stack = NULL;
#else
__thread Except_Frame *Except_stack = NULL;
#endif
const Except_T Assert_Failed = {"Assertion failed"};
void Except_raise(const T *e, const char *file, int line)
{
Except_Frame *p;
/* IMPORTANT: Caller must ensure 'e' points to valid memory for the duration
* of exception propagation. Typically, exceptions are static/global constants
* (e.g., Socket_Failed) or have thread-local storage duration. Stack-allocated
* exceptions are unsafe and will cause undefined behavior. */
if (e == NULL)
{
fprintf(stderr, "FATAL: Except_raise called with NULL exception pointer\n");
fprintf(stderr, "aborting...\n");
fflush(stderr);
abort();
}
p = Except_stack;
if (p == NULL)
{
fprintf(stderr, "Uncaught exception");
if (e->reason)
fprintf(stderr, ": %s", e->reason);
else
fprintf(stderr, ": (no reason provided)");
if (file)
{
if (line > 0)
fprintf(stderr, " raised at %s:%d\n", file, line);
else
fprintf(stderr, " raised at %s\n", file);
}
else if (line > 0)
{
fprintf(stderr, " raised at line %d\n", line);
}
else
{
fprintf(stderr, " (location unknown)\n");
}
fprintf(stderr, "aborting...\n");
fflush(stderr);
abort();
}
p->exception = e;
p->file = file ? file : "unknown";
p->line = line > 0 ? line : 0;
/* Pop the exception frame before longjmp */
Except_stack = p->prev;
/* Jump directly using the original jmp_buf */
longjmp(p->env, Except_raised);
}
#undef T
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment