From ade8e8fcacd9e10da9ed260ff6c0a096b447b113 Mon Sep 17 00:00:00 2001 From: Randy Jordan Date: Tue, 21 Apr 2026 19:01:29 -0500 Subject: [PATCH] Exceptions & Try/Catch --- README.md | 17 ++++- include/ci2.h | 1 - include/ci2_exception.h | 107 +++++++++++++++++++++++++++++ src/linux_exception.c | 37 ++++++++++ src/win32_exception.c | 69 +++++++++++++++++++ tests/01_sanity.c | 13 ++++ tests/02_uncaught.c | 14 ++++ tests/{01_window.c => 99_window.c} | 0 8 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 include/ci2_exception.h create mode 100644 src/linux_exception.c create mode 100644 src/win32_exception.c create mode 100644 tests/01_sanity.c create mode 100644 tests/02_uncaught.c rename tests/{01_window.c => 99_window.c} (100%) diff --git a/README.md b/README.md index bfd3437..91eb895 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,23 @@ but I hope to improve it overtime. * [License](#license) ## Features +- [x] Basic Window (Win32 & Linux) + +`Currently finished a basic window platform layer. The focus is going to be code +for CLI tools but, I wanted to be able to open a simple window. It is a great +example case too as the OS API's are slightly different, so a platform layer +is a great fix. +` +- [x] Exceptions & Try/Catch + +` Finished adding Exceptions and Try/Catch. This was the worst example for a +platform layers as these are just clever macros. From what I can read, this +approach to error handling is generally frowned upon due to debugging isssues.` ## Todos -- [ ] Better makefile + +- [?] Better makefile - [ ] Better Usage Examples -- [ ] Basic Window -- [ ] Exceptions & Try/Catch - [ ] Memory Allocator - [ ] Arena Allocator - [ ] Arrays diff --git a/include/ci2.h b/include/ci2.h index 408dac9..0fb6a7b 100644 --- a/include/ci2.h +++ b/include/ci2.h @@ -1,6 +1,5 @@ #ifndef CI2_INCLUDED #define CI2_INCLUDED -extern void ci2_printf() #endif diff --git a/include/ci2_exception.h b/include/ci2_exception.h new file mode 100644 index 0000000..2a0e16d --- /dev/null +++ b/include/ci2_exception.h @@ -0,0 +1,107 @@ +#ifndef CI2_EXCEPTION_H +#define CI2_EXCEPTION_H + +#include +#include + +struct Exception { + const char *reason; +}; +typedef struct Exception Exception; + +struct Except_Frame { + struct Except_Frame *prev; + jmp_buf env; + const char *file; + int line; + const struct Exception *exception; +}; +typedef struct Except_Frame Except_Frame; + +enum { EXCEPT_STATE_ENTERED=0, + EXCEPT_STATE_RAISED, + EXCEPT_STATE_HANDLED, + EXCEPT_STATE_FINALIZED, + EXCEPT_STATE_COUNT +}; + +extern struct Except_Frame *except_stack; +extern const struct Exception assertion_failed; +extern void except_raise(const struct Exception *e, const char *file, int line); + +#ifdef WIN32 +#include +extern DWORD except_index; +extern void except_init(void); +extern void except_push(Except_Frame *fp); +extern void except_pop(void); + +#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_pop(),0) default: return +#define TRY do { \ + volatile int except_flag; \ + Except_Frame except_frame; \ + if (except_index == TLS_OUT_OF_INDEXES) \ + except_init(); \ + except_push(&except_frame); \ + except_flag = setjmp(except_frame.env); \ + if (except_flag == EXCEPT_STATE_ENTERED) { +#define EXCEPT(e) \ + if (except_flag == EXCEPT_STATE_ENTERED) except_pop(); \ + } else if (except_frame.exception == &(e)) { \ + except_flag = EXCEPT_STATE_HANDLED; +#define ELSE \ + if (except_flag == EXCEPT_STATE_ENTERED) except_pop(); \ + } else { \ + except_flag = EXCEPT_STATE_HANDLED; +#define FINALLY \ + if (except_flag == EXCEPT_STATE_ENTERED) except_pop(); \ + } { \ + if (except_flag == EXCEPT_STATE_ENTERED) \ + except_flag = EXCEPT_STATE_FINALIZED; +#define END_TRY \ + if (except_flag == EXCEPT_STATE_ENTERED) except_pop(); \ + } if (except_flag == EXCEPT_STATE_RAISED) RERAISE; \ +} while (0) +#else +#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_stack = &except_frame; \ + except_flag = setjmp(except_frame.env); \ + if (except_flag == EXCEPT_STATE_ENTERED) { +#define EXCEPT(e) \ + if (except_flag == EXCEPT_STATE_ENTERED) except_stack = except_stack->prev; \ + } else if (except_frame.exception == &(e)) { \ + except_flag = EXCEPT_STATE_HANDLED; +#define ELSE \ + if (except_flag == EXCEPT_STATE_ENTERED) except_stack = except_stack->prev; \ + } else { \ + except_flag = EXCEPT_STATE_HANDLED; +#define FINALLY \ + if (except_flag == EXCEPT_STATE_ENTERED) except_stack = except_stack->prev; \ + } { \ + if (except_flag == EXCEPT_STATE_ENTERED) \ + except_flag = EXCEPT_STATE_FINALIZED; +#define END_TRY \ + if (except_flag == EXCEPT_STATE_ENTERED) except_stack = except_stack->prev; \ + } if (except_flag == EXCEPT_STATE_RAISED) RERAISE; \ +} while (0) +#endif + +#undef assert +#ifdef NDEBUG +#define assert(e) ((void)0) +#else +extern void ci2_assert(int e); +#define assert(e) ((void)((e)||(RAISE(assertion_failed),0))) +#endif + +#endif // CI2_EXCEPTION_H diff --git a/src/linux_exception.c b/src/linux_exception.c new file mode 100644 index 0000000..b30c4e6 --- /dev/null +++ b/src/linux_exception.c @@ -0,0 +1,37 @@ +#include +#include +#include "../include/ci2_exception.h" + +struct Except_Frame *except_stack = NULL; +const struct Exception assertion_failed = { "Assertion failed" }; +void ci2_assert(int e){ + if(!e){ + RAISE(assertion_failed); + } +} + +void except_raise(const struct Exception *e, const char *file,int line){ + + struct Except_Frame *p = except_stack; + ci2_assert(e != NULL); + + if (p == NULL) { + fprintf(stderr, "Uncaught exception"); + if (e->reason) + fprintf(stderr, " %s", e->reason); + else + fprintf(stderr, " at 0x%p", (void*)e); + if (file && line > 0) + fprintf(stderr, " raised at %s:%d\n", file, line); + fprintf(stderr, "aborting...\n"); + fflush(stderr); + abort(); + } + + p->exception = e; + p->file = file; + p->line = line; + except_stack = except_stack->prev; + + longjmp(p->env, EXCEPT_STATE_RAISED); +} diff --git a/src/win32_exception.c b/src/win32_exception.c new file mode 100644 index 0000000..8e00291 --- /dev/null +++ b/src/win32_exception.c @@ -0,0 +1,69 @@ +#include +#include +#include "../include/win32_exception.h" + +DWORD except_index = -1; + +const struct Exception assertion_failed = { "Assertion failed" }; +void ci2_assert(int e){ + if(!e){ + RAISE(assertion_failed); + } +} + +#ifdef WIN32 +_CRTIMP void __cdecl _assert(void *, void *, unsigned); +#undef assert +#define ci2_assert(e) ((e) || (_assert(#e, __FILE__, __LINE__), 0)) +#endif + +void except_raise(const struct Exception *e, const char *file,int line){ + Except_Frame *p; + + if (except_index == TLS_OUT_OF_INDEXES) + except_init(); + p = TlsGetValue(except_index); + ci2_assert(e != NULL); + if (p == NULL) { + fprintf(stderr, "Uncaught exception"); + if (e->reason) + fprintf(stderr, " %s", e->reason); + else + fprintf(stderr, " at 0x%p", (void*)e); + if (file && line > 0) + fprintf(stderr, " raised at %s:%d\n", file, line); + fprintf(stderr, "aborting...\n"); + fflush(stderr); + abort(); + } + + p->exception = e; + p->file = file; + p->line = line; + + except_pop(); + + longjmp(p->env, EXCEPT_STATE_RAISED); +} +void except_init(void) { + BOOL cond; + except_index = TlsAlloc(); + assert(except_index != TLS_OUT_OF_INDEXES); + cond = TlsSetValue(except_index, NULL); + assert(cond == TRUE); +} +void except_push(Except_Frame *fp) { + BOOL cond; + + fp->prev = TlsGetValue(except_index); + cond = TlsSetValue(except_index, fp); + assert(cond == TRUE); +} +void except_pop(void) { + BOOL cond; + Except_Frame *tos = TlsGetValue(except_index); + + cond = TlsSetValue(except_index, tos->prev); + assert(cond == TRUE); +} + diff --git a/tests/01_sanity.c b/tests/01_sanity.c new file mode 100644 index 0000000..07026c0 --- /dev/null +++ b/tests/01_sanity.c @@ -0,0 +1,13 @@ +#define DEBUG +#include "../include/ci2_exception.h" + +#include +#include + +int main(void){ + printf("\n\n"); + printf("Assertion sanity test.\n"); + ci2_assert(0); + return EXIT_SUCCESS; + +} diff --git a/tests/02_uncaught.c b/tests/02_uncaught.c new file mode 100644 index 0000000..0287af1 --- /dev/null +++ b/tests/02_uncaught.c @@ -0,0 +1,14 @@ +#define DEBUG +#include "../include/ci2_exception.h" + +#include +#include + +int main(void){ + printf("\n\n"); + printf("Uncaught exceptions sanity test.\n"); + struct Exception uncaught = {NULL}; + RAISE(uncaught); + return EXIT_SUCCESS; + +} diff --git a/tests/01_window.c b/tests/99_window.c similarity index 100% rename from tests/01_window.c rename to tests/99_window.c