Created
October 26, 2025 08:28
-
-
Save 7etsuo/2a93320e011c12ee22683e7874c7b681 to your computer and use it in GitHub Desktop.
custom exception handler in C using setjmp/longjmp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //// 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