Created
November 8, 2011 15:31
-
-
Save kulp/1348052 to your computer and use it in GitHub Desktop.
A toy exception handling system for C
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
sauf is a lightweight exception handling system for C. | |
The original implementation idea came from an article in Dr. | |
Dobb's Journal : http://www.on-time.com/ddj0011.htm | |
Some implementation details were taken from the excc project : | |
http://adomas.org/excc/ . This project was almost sufficient to my | |
requirements, but it requires GCC (sauf aims to work with any C99- | |
compliant compiler) and does not support 'finally' blocks. | |
The design goals of the sauf project are portability, robustness | |
(absolutely correct behavior) and lightweightness. Portability in this | |
context means that sauf will strive to depend upon no behavior that is | |
not guaranteed by the C99 standard. Correct behavior is a less easily | |
proven goal; the gradual growth of the test suite attemps to address | |
this. The lightweightness of the project (no dynamically-allocated | |
memory, tiny library size) strives to make it suitable for every | |
application, including, for example, embedded systems. | |
The code is admittedly ugly and difficult to read, due to its heavy | |
reliance upon macros and infrequently-used "features" of the C standard. | |
Over time it is hoped that functionality will be moved into (inline) | |
functions where possible in order to improve clarity and modularity. | |
The name 'sauf' comes from the French word meaning 'except' or | |
'safe'. Both of these meanings are appropriate in the context of | |
exception handling. An approximate pronunciation for English speakers is | |
"sohf" with a long O sound. | |
Darren Kulp | |
2009-10-02 | |
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
/** | |
* @file | |
* A lightweight exception-handling system for C. | |
*/ | |
#include "sauf.h" | |
#include <stdarg.h> | |
#include <stdio.h> | |
/// @todo rename __trace to something sauf-specific | |
#if !defined(__trace) && SAUF_TRACE | |
#define __trace(...) _sauf__trace(__func__, __VA_ARGS__) | |
#else | |
#define __trace(...) | |
#endif | |
/// @todo handle thread-local storage generically | |
/*__thread*/ volatile struct _sauf_link * volatile _sauf_curr, | |
* volatile _sauf_old; | |
/// @todo rewrite to show where I am in the call tree | |
void _sauf__trace(const char *func, const char *fmt, ...) | |
{ | |
printf("%s:", func); | |
va_list vl; | |
va_start(vl, fmt); | |
vprintf(fmt, vl); | |
va_end(vl); | |
putchar('\n'); | |
} | |
void _sauf_rethrower(volatile struct _sauf_state *_sauf_state) | |
{ | |
/// @todo test this code path | |
if (_sauf_state->rc == SAUF_EX_BROKEN) | |
return; | |
_sauf_curr = _sauf_state->saved; | |
__trace("__otherwise"); | |
if (_sauf_curr) { | |
__trace("rethrowing: %d", _sauf_state->rc); | |
_sauf_state->rethrow = _sauf_state->rc; | |
_sauf_longjmp(_sauf_state->jb, SAUF_EX_FINALLY); | |
} else { | |
__trace("unhandled: %d", _sauf_state->rc); | |
_sauf_longjmp(_sauf_state->jb, SAUF_EX_FINALLY); | |
} | |
} | |
void _sauf_unwind(volatile struct _sauf_state *_sauf_state) | |
{ | |
__trace("popping exception frame"); | |
_sauf_curr = _sauf_state->saved; | |
if (_sauf_curr) { | |
__trace("[not at top frame]"); | |
_sauf_state->jb = &_sauf_curr->curr; | |
} | |
} | |
struct _sauf_state _sauf_setup_struct(const char *file, const char *func, long line, ...) | |
{ | |
return (struct _sauf_state) { | |
.saved = _sauf_old, | |
.jb = &_sauf_curr->curr, | |
.state = 0, | |
.frame = { | |
.next = _sauf_old, | |
/// @todo make these fields optional to save space | |
.func = func, | |
.file = file, | |
.line = line, | |
}, | |
}; | |
} | |
void _sauf_teardown(volatile struct _sauf_state *_sauf_state) | |
{ | |
_sauf_state->state = -1; | |
_sauf_curr = _sauf_state->saved; | |
_sauf_state->jb = _sauf_curr ? &_sauf_curr->curr : _sauf_state->jb; | |
if (_sauf_state->rethrow) | |
_sauf_longjmp(_sauf_state->jb, _sauf_state->rethrow); | |
} | |
void _sauf_longjmp(volatile jmp_buf *px, int val) | |
{ | |
if (px) | |
longjmp(*(jmp_buf*)px, val); | |
else | |
// if there is no enclosing scope to catch me, abort() | |
/// @todo change abort() to a user-specifiable function macro | |
abort(); | |
} | |
/* vim:set et ts=4 sw=4 syntax=c.doxygen: */ |
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
/** | |
* @file | |
* A lightweight exception-handling system for C. | |
*/ | |
#ifndef SAUF_H_ | |
#define SAUF_H_ | |
#include <setjmp.h> | |
// for abort() | |
#include <stdlib.h> | |
//------------------------------------------------------------------------------ | |
// Public "API" | |
//------------------------------------------------------------------------------ | |
#define __except \ | |
for (volatile struct _sauf_state _sauf_state = (_sauf_old = _sauf_curr, \ | |
_sauf_curr = &_sauf_state.frame, \ | |
_sauf_setup_struct(__FILE__, __func__, __LINE__)); \ | |
_sauf_state.state != -1; \ | |
_sauf_teardown(&_sauf_state)) \ | |
switch ((_sauf_state.rc = _sauf_setjmp(_sauf_state.jb))) | |
#define __catch(X) \ | |
_sauf_longjmp(_sauf_state.jb, SAUF_EX_FINALLY); break; \ | |
case X: _sauf_catch; | |
#define __finally \ | |
_sauf_longjmp(_sauf_state.jb, SAUF_EX_FINALLY); break; \ | |
default: _sauf_rethrower(&_sauf_state); break; \ | |
case SAUF_EX_FINALLY: _sauf_unwind(&_sauf_state); | |
#define __first case SAUF_EX_FIRST: | |
#define __throw(X) _sauf_longjmp(_sauf_state.jb, X) | |
//------------------------------------------------------------------------------ | |
// Private "API" | |
//------------------------------------------------------------------------------ | |
// if SAUF_EXCEPTIONS is defined as a comma-separated list of enum values, it | |
// will be substituted here ; otherwise, it's just an unused enum value | |
enum exceptions { | |
SAUF_EX_BROKEN=-2, | |
SAUF_EX_FINALLY=-1, | |
SAUF_EX_FIRST=0, | |
SAUF_EXCEPTIONS | |
}; | |
struct _sauf_link { | |
volatile jmp_buf curr; | |
const char *file; ///< throw location (file) | |
const char *func; ///< throw location (function) | |
long line; ///< throw location (line) | |
/// @todo remove this if we are not going to use it anywhere | |
volatile struct _sauf_link * volatile next; | |
}; | |
/// @todo remove volatiles where possible | |
struct _sauf_state { | |
/// the result of setjmp() | |
volatile int rc; | |
/// an exception to rethrow after __finally, if non-zero | |
volatile int rethrow; | |
/// controls for()-loop subversion | |
volatile int state; | |
/// saved state | |
volatile struct _sauf_link * volatile saved; | |
/// temporary link story | |
volatile struct _sauf_link frame; | |
/// used to subvert struct initialization to run imperative code | |
volatile void *scratch; | |
/// pointer to the parent jump-buffer to longjmp() to | |
volatile jmp_buf * volatile jb; | |
}; | |
#define _sauf_setjmp(px) ((px) ? setjmp(*((jmp_buf*)px)) : SAUF_EX_BROKEN) | |
#define _sauf_catch _sauf_curr = _sauf_state.saved | |
extern volatile struct _sauf_link * volatile _sauf_curr, * volatile _sauf_old; | |
void _sauf_longjmp(volatile jmp_buf *px, int val); | |
void _sauf_rethrower(volatile struct _sauf_state *_sauf_state); | |
void _sauf_unwind(volatile struct _sauf_state *_sauf_state); | |
// intentionally declared as varargs so we can call it how we want for | |
// side-effects | |
struct _sauf_state _sauf_setup_struct(const char *file, const char *func, long line, ...); | |
void _sauf_teardown(volatile struct _sauf_state *_sauf_state); | |
void _sauf__trace(const char *func, const char *fmt, ...); | |
#endif /* SAUF_H_ */ | |
/* vim:set et ts=4 sw=4 syntax=c.doxygen: */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment