From 26179d76aa23f465c43341bf686688e9ebc413f1 Mon Sep 17 00:00:00 2001 From: VegOwOtenks Date: Fri, 13 Jun 2025 16:30:32 +0200 Subject: [PATCH] Exceptions in C! --- src/CMakeLists.txt | 1 + src/ExceptionalScope/CMakeLists.txt | 1 + src/ExceptionalScope/ExceptionalScope.c | 51 +++++++++++++++++++++++++ src/ExceptionalScope/ExceptionalScope.h | 33 ++++++++++++++++ tests/CMakeLists.txt | 4 ++ tests/ExceptionalScope.test.c | 44 +++++++++++++++++++++ 6 files changed, 134 insertions(+) create mode 100644 src/ExceptionalScope/CMakeLists.txt create mode 100644 src/ExceptionalScope/ExceptionalScope.c create mode 100644 src/ExceptionalScope/ExceptionalScope.h create mode 100644 tests/ExceptionalScope.test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 029852b..2aeee64 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,3 +20,4 @@ add_subdirectory(./BinaryTree) add_subdirectory(./Subprocess) add_subdirectory(./TracingHeap) add_subdirectory(./Complex) +add_subdirectory(./ExceptionalScope) diff --git a/src/ExceptionalScope/CMakeLists.txt b/src/ExceptionalScope/CMakeLists.txt new file mode 100644 index 0000000..8694718 --- /dev/null +++ b/src/ExceptionalScope/CMakeLists.txt @@ -0,0 +1 @@ +add_library(ExceptionalScope STATIC ExceptionalScope.c) diff --git a/src/ExceptionalScope/ExceptionalScope.c b/src/ExceptionalScope/ExceptionalScope.c new file mode 100644 index 0000000..d901b02 --- /dev/null +++ b/src/ExceptionalScope/ExceptionalScope.c @@ -0,0 +1,51 @@ +#include "ExceptionalScope.h" +#include "../errorcodes.h" + +#include +#include +#include + + +int ExceptionalScope_Create(ExceptionalScope* self) +{ + memset(self, 0, sizeof(*self)); + + return EXIT_SUCCESS; +} + +void ExceptionalScope_ActivateCleaners(ExceptionalScope* self) +{ + while (self->cleaners != NULL) { + self->cleaners->action(self->cleaners->arg); + self->cleaners = self->cleaners->next; + } +} + +void ExceptionalScope_Destroy(ExceptionalScope* self) +{ + ExceptionalScope_ActivateCleaners(self); +} + +void ExceptionalScope_PushCleaner(ExceptionalScope* self, ExceptionalScopeCleaner* cleaner) +{ + cleaner->next = self->cleaners; + self->cleaners = cleaner; +} + +int ExceptionalScope_PopCleaner(ExceptionalScope* self, ExceptionalScopeCleaner* cleaner) +{ + if (self->cleaners != cleaner) return ENOTFOUND; + + self->cleaners = self->cleaners->next; + + return EXIT_SUCCESS; +} + +[[noreturn]] void ExceptionalScope_Throw(ExceptionalScope* self, void* exception) +{ + self->exception = exception; + + ExceptionalScope_ActivateCleaners(self); + + longjmp(self->handler, 1); +} diff --git a/src/ExceptionalScope/ExceptionalScope.h b/src/ExceptionalScope/ExceptionalScope.h new file mode 100644 index 0000000..5a1c567 --- /dev/null +++ b/src/ExceptionalScope/ExceptionalScope.h @@ -0,0 +1,33 @@ +#ifndef UTILITIEC_EXCEPTIONALSCOPE_H +#define UTILITIEC_EXCEPTIONALSCOPE_H + +#include + +#define withScopedCleaner(scope, cleaner) for (int __cleanerDone##cleaner = (ExceptionalScope_PushCleaner(scope, &cleaner), 1); __cleanerDone##cleaner--; ExceptionalScope_PopCleaner(scope, &cleaner)) +#define try(scope) if (setjmp((scope)->handler) == 0) +#define catch else + +typedef void (*ExceptionalScopeCleanerFunction) (void* arg); + +typedef struct ExceptionalScopeCleaner_s { + ExceptionalScopeCleanerFunction action; + void* arg; + + struct ExceptionalScopeCleaner_s* next; +} ExceptionalScopeCleaner; + +typedef struct ExceptionalScope_s { + jmp_buf handler; // jump here when the exception is thrown + ExceptionalScopeCleaner* cleaners; // execute all these before the jumping to the handler + void* exception; +} ExceptionalScope; + +int ExceptionalScope_Create(ExceptionalScope* self); +void ExceptionalScope_Destroy(ExceptionalScope* self); + +void ExceptionalScope_PushCleaner(ExceptionalScope* self, ExceptionalScopeCleaner* cleaner); +int ExceptionalScope_PopCleaner(ExceptionalScope* self, ExceptionalScopeCleaner* cleaner); + +[[noreturn]] void ExceptionalScope_Throw(ExceptionalScope* self, void* exception); + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f8b9158..ccd0b28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -60,4 +60,8 @@ target_link_libraries(QuadTree-test rand pointers allocator-interface + +add_executable(ExceptionalScope-test ExceptionalScope.test.c) +target_link_libraries(ExceptionalScope-test + ExceptionalScope ) diff --git a/tests/ExceptionalScope.test.c b/tests/ExceptionalScope.test.c new file mode 100644 index 0000000..0bbaa87 --- /dev/null +++ b/tests/ExceptionalScope.test.c @@ -0,0 +1,44 @@ +#include "../src/ExceptionalScope/ExceptionalScope.h" +#include + +#define unsafe + +void print(const char* s) +{ + unsafe { + puts(s); + } +} + +int main() +{ + ExceptionalScope scope; + ExceptionalScope_Create(&scope); + scope.exception = (void*) 0xCAFEBABE; + + try(&scope) { + ExceptionalScopeCleaner outerCleaner = { + .next = NULL, + .arg = "Unwinding the cleaner stack #2", + .action = (ExceptionalScopeCleanerFunction) print, + }; + withScopedCleaner(&scope, outerCleaner) { + ExceptionalScopeCleaner innerCleaner = { + .next = NULL, + .arg = "Unwinding the cleaner stack #1", + .action = (ExceptionalScopeCleanerFunction) print, + }; + + withScopedCleaner(&scope, innerCleaner) { + + ExceptionalScope_Throw(&scope, (void*) 0xDEADBEEF); + puts("After the throw"); + + } + } + } catch { + printf("Caught an exception: %p\n", scope.exception); + } + + ExceptionalScope_Destroy(&scope); +}