Skip to content

Instantly share code, notes, and snippets.

@iafisher
Created June 10, 2019 14:35
Show Gist options
  • Save iafisher/7b1b7c3b69a5ecccfffba199360cc7d1 to your computer and use it in GitHub Desktop.
Save iafisher/7b1b7c3b69a5ecccfffba199360cc7d1 to your computer and use it in GitHub Desktop.
Exception handling with try, catch and throw statements, implemented in pure C99
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
const char* e = NULL;
#define EXCEPTION_DEPTH 128
jmp_buf* iafisher_trycatch_stack[EXCEPTION_DEPTH];
size_t iafisher_trycatch_stack_len = 0;
void iafisher_trycatch_push(jmp_buf* jbuf) {
if (iafisher_trycatch_stack_len == EXCEPTION_DEPTH) {
fprintf(stderr, "Error in iafisher/trycatch: exceeded max exceptions\n");
exit(EXIT_FAILURE);
}
iafisher_trycatch_stack[iafisher_trycatch_stack_len++] = jbuf;
}
jmp_buf* iafisher_trycatch_pop(void) {
if (!iafisher_trycatch_stack_len) {
fprintf(stderr, "Uncaught exception from iafisher/trycatch library: %s\n", e);
exit(EXIT_FAILURE);
}
return iafisher_trycatch_stack[--iafisher_trycatch_stack_len];
}
/**
* A toy library for throwing and catching exceptions in C with a Python-like
* syntax.
*
*
* EXAMPLE
**********
*
* #include <stdio.h>
* #include "trycatch.h"
*
* int bar() {
* throw ("bar could not compute");
* return 41;
* }
*
* int foo() {
* try {
* return bar() + 1;
* } catch {
* printf("foo caught error: %s\n", e);
* throw ("foo could not compute");
* }
* } *
* int main() {
* try {
* int x = foo();
* printf("Result: %d\n", x);
* } catch {
* printf("main caught error: %s\n", e);
* }
* }
*
*
* DOCUMENTATION
****************
*
* You can use this library by including "trycatch.h" and adding "trycatch.c"
* to your build system, although this is a toy library which I don't recommend
* using in production.
*
* When you want to throw an exception, use the `throw` macro:
*
* throw ("something went wrong");
*
* The argument to `throw` is a string that is made available to the `catch`
* clause that catches the exception.
*
* To catch an exception, use the `try` and `catch` macros:
*
* try {
* subroutine_that_may_fail();
* } catch {
* printf("Error: %s\n", e);
* }
*
* If any function in the `try` clause throws an exception, then the `catch`
* clause is executed. Otherwise, the `catch` clause is not executed and
* the program continues normally.
*
* In the `catch` clause, a string variable `e` is available which holds the
* message passed to the `throw` exception.
*
* The function that throws does not have to be directly called by the
* function with the try-catch statement, i.e. if `f`, which has a try-catch
* statement, calls `g`, which in turns calls a function `h` which throws an
* exception, then `f` will catch the exception, skipping over `g`.
*
* Technical limitations:
* - You cannot catch an exception thrown in a different thread.
* - You cannot exceed a certain fixed number of active try blocks at any
* one time.
*
* Author: Ian Fisher (iafisher@protonmail.com)
* Version: June 2019
*/
#ifndef IAFISHER_TRYCATCH_H
#define IAFISHER_TRYCATCH_H
#include <setjmp.h>
#define try \
jmp_buf jmp_buffer_name; \
iafisher_trycatch_push(&jmp_buffer_name); \
int setjmp_result_name = setjmp(jmp_buffer_name); \
if (setjmp_result_name == 0) \
#define catch else
#define throw(x) \
do { e = x; longjmp(*iafisher_trycatch_pop(), 0); } while (0)
extern const char* e;
/* These functions, macros and variables are for internal use only. */
#define tokenpaste(x, y) x ## y
#define tokenpaste2(x, y) tokenpaste(x, y)
#define jmp_buffer_name \
tokenpaste2(iafisher_trycatch_jmp_buffer, __LINE__)
#define setjmp_result_name \
tokenpaste2(iafisher_trycatch_setjmp_result, __LINE__)
extern void iafisher_trycatch_push(jmp_buf*);
extern jmp_buf* iafisher_trycatch_pop(void);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment