Z
Zygmunt Krynicki
I'd like to hear your suggestions / opinions about this code.
I'm planning to use this libary in my C application because my error recovery code is
getting very complex and exceptions simplify things greatly.
I've prepared a small package avalaible at
http://lexx.eu.org/~zyga/exceptions.tar.gz
For those of you who don't want to click that much I'm including the
readme file and the example here:
README (cut here)
---[ EXPERIMENTAL EXCEPTION HANDLING ROUTINES FOR C ]---
Contents:
0) AUTHOR
1) REQUIREMENTS
2) FEATURES
3) GOOD THINGS
4) BAD THINGS
5) USAGE
5.0) INITIALIZING EXCEPTION HANDLING
5.1) DEFINING EXCEPTIONS
5.2) REGISTERING EXCEPTIONS
5.3) THROWING EXCEPTIONS
5.4) CATCHING EXCEPTIONS
0) AUTHOR
Zygmunt Krynicki
(e-mail address removed) (or (e-mail address removed))
1) REQUIREMENTS
* setjmp, longjmp
* variadic marcos
* some GNU attrbutes used (alghough they are not required)
* __attribute__((noreturn))
I've set this for `throw' and `rethrow'
* __attribute__((unused))
I've set this for all pointers to exception data
to silence warrnings about unused variables
Tested on Slackware 3.2.2 with gcc-3.2.2
2) FEATURES
* you can throw exceptions
* `try' block for executing dangerous code
* `catch' block grabs specific exception
* `catch_rest' block grabs any ungrabbed exception
* `finally' block for any cleanup neccesary
* simple exception definition and registration
3) GOOD THINGS
* you can have multiple exceptions
* you can nest try blocks
* exceptions can be thrown from anywhere
* centralized cleanup code for the entire function
* working exception handling for C
* could be modified a little to support multi-threading
4) BAD THINGS
* no exception inheritance
* need of unique identifiers for exceptions that makes building libaries impossible
they could be replaced by string identifiers
but exception lookup would be both more difficult and slower
(on the other hand that would allow some kind of exception inheritance)
* cleanup code is not going to execute if your catch code throws (or rethrows) an exception
5) USAGE
5.0) INITIALIZING EXCEPTION HANDLING
EXCEPTION_INIT (stack_pointer, size_of_stack);
Pretty simple, just call this function before you use any exceptions.
An obvious place to do this would be somewhere at the beggining of main.
Example:
EXCEPTION_INIT ( malloc(1024), 1024);
The amount of memory you assign will be used to hold exception data.
It should be enough if you use sizeof(largest_exception+ sizeof(int))
5.1) DEFINING EXCEPTIONS
Note: there are no semicolons after these macros.
EXCEPTION_DEFINE (type, some_unique_interer)
/* any field declarations go here */
int foo;
EXCEPTION_CTOR (type)
/* exception constructor, arguments avalaible as va_list, 'this' pointer avalaible as EXCEPTION */
EXCEPTION->foo = va_arg (EXCEPTION_ARG, int);
EXCEPTION_DTOR (type)
/* exception destructor */
EXCEPTION_DONE
5.2) REGISTERING EXCEPTIONS
Call this function olny after you've called EXCEPTION_INIT
REGISTER_EXCEPTION (type);
5.3) THROWING EXCEPTIONS
There are two functions: throw and vthrow. Both take type of exception as it's first argument.
throw (type, arg1, arg2, ...);
vthrow (type, argumenst_as_va_list);
5.4) CATCHING EXCEPTIONS
Minimal version, try to do something dangerous:
try {
/* dangerous code */
} finally {
/* cleanup code */
} untry;
More complicated version, catch any exceptions thrown:
try {
/* dangerous code */
} catch_rest {
/* code executed if any exception was thrown */
} finally {
/* cleanup code */
} untry;
Even more compliacted: catch specific exceptions:
try {
dangerous code
} catch (type) {
/* code executed when exception TYPE was thrown,
data avalaible as EXCEPTION (pointer to struct) */
prinf ("foo: %d\n", EXCEPTION->some_field);
} finally {
/* cleanup code */
} untry;
Full version:
try {
/* dangerous code */
} catch (type) {
/* code executed when exception TYPE was thrown,
data avalaible as EXCEPTION (pointer to struct) */
prinf ("foo: %d\n", EXCEPTION->some_field);
} catch_rest {
/* code executed if exception was thrown
but could not be handled by any specific catch */
} finally {
/* cleanup code */
} untry;
EXAMPLE: (cut here)
#include <stdio.h>
#include <stdlib.h>
#define LOWERCASE_EXCEPTIONS
#include "exception.h"
enum MY_EXCEPTIONS { EXCEPTION_SOME = 1, EXCEPTION_OTHER };
/* -------------- */
/* exception definitions no semicolons after macros! */
EXCEPTION_DEFINE (some, EXCEPTION_SOME)
int foo;
EXCEPTION_CTOR (some)
EXCEPTION->foo = va_arg (EXCEPTION_ARG, int);
printf ("some: ctor, foo: %d\n", EXCEPTION->foo);
EXCEPTION_DTOR (some)
printf ("some: dtor\n");
EXCEPTION_DONE
/* ----------- */
EXCEPTION_DEFINE (other, EXCEPTION_OTHER)
EXCEPTION_CTOR (other)
printf ("other: ctor\n");
EXCEPTION_DTOR (other)
printf ("other: dtor\n");
EXCEPTION_DONE
/* ----------- */
void danger()
{
printf ("danger: entering\n");
throw (some, 10);
printf ("danger: exiting\n");
}
void inner()
{
char *buf;
printf("inner: entering\n");
buf = malloc(100);
try {
danger();
} finally {
printf ("inner: cleanup\n");
free (buf);
} untry;
printf ("inner: exiting\n");
}
int main(void)
{
EXCEPTION_INIT (malloc(1024), 1024); /* exception stack */
REGISTER_EXCEPTION (some);
REGISTER_EXCEPTION (other); /* registering exceptions */
try {
printf ("main: try block\n");
try {
printf ("main, nested: try block\n");
throw (other);
} catch (other) {
printf ("main, nested: cought other\n");
} finally {
printf ("main, nested cleanup\n");
} untry;
printf ("main: again in try block\n");
inner();
} catch (some) {
printf ("main: cought some, foo: %d\n", EXCEPTION->foo);
} catch (other) {
printf ("main: cough other\n");
} catch_rest {
printf ("main: cought unknown exception\n");
} finally {
printf ("main: cleanup\n");
} untry;
return EXIT_SUCCESS;
}
OUTPUT FROM THE EXAMPLE:
main: try block
main, nested: try block
other: ctor
main, nested: cought other
main, nested cleanup
other: dtor
main: again in try block
inner: entering
danger: entering
some: ctor, foo: 10
inner: cleanup
main: cought some, foo: 10
main: cleanup
some: dtor
I'm planning to use this libary in my C application because my error recovery code is
getting very complex and exceptions simplify things greatly.
I've prepared a small package avalaible at
http://lexx.eu.org/~zyga/exceptions.tar.gz
For those of you who don't want to click that much I'm including the
readme file and the example here:
README (cut here)
---[ EXPERIMENTAL EXCEPTION HANDLING ROUTINES FOR C ]---
Contents:
0) AUTHOR
1) REQUIREMENTS
2) FEATURES
3) GOOD THINGS
4) BAD THINGS
5) USAGE
5.0) INITIALIZING EXCEPTION HANDLING
5.1) DEFINING EXCEPTIONS
5.2) REGISTERING EXCEPTIONS
5.3) THROWING EXCEPTIONS
5.4) CATCHING EXCEPTIONS
0) AUTHOR
Zygmunt Krynicki
(e-mail address removed) (or (e-mail address removed))
1) REQUIREMENTS
* setjmp, longjmp
* variadic marcos
* some GNU attrbutes used (alghough they are not required)
* __attribute__((noreturn))
I've set this for `throw' and `rethrow'
* __attribute__((unused))
I've set this for all pointers to exception data
to silence warrnings about unused variables
Tested on Slackware 3.2.2 with gcc-3.2.2
2) FEATURES
* you can throw exceptions
* `try' block for executing dangerous code
* `catch' block grabs specific exception
* `catch_rest' block grabs any ungrabbed exception
* `finally' block for any cleanup neccesary
* simple exception definition and registration
3) GOOD THINGS
* you can have multiple exceptions
* you can nest try blocks
* exceptions can be thrown from anywhere
* centralized cleanup code for the entire function
* working exception handling for C
* could be modified a little to support multi-threading
4) BAD THINGS
* no exception inheritance
* need of unique identifiers for exceptions that makes building libaries impossible
they could be replaced by string identifiers
but exception lookup would be both more difficult and slower
(on the other hand that would allow some kind of exception inheritance)
* cleanup code is not going to execute if your catch code throws (or rethrows) an exception
5) USAGE
5.0) INITIALIZING EXCEPTION HANDLING
EXCEPTION_INIT (stack_pointer, size_of_stack);
Pretty simple, just call this function before you use any exceptions.
An obvious place to do this would be somewhere at the beggining of main.
Example:
EXCEPTION_INIT ( malloc(1024), 1024);
The amount of memory you assign will be used to hold exception data.
It should be enough if you use sizeof(largest_exception+ sizeof(int))
5.1) DEFINING EXCEPTIONS
Note: there are no semicolons after these macros.
EXCEPTION_DEFINE (type, some_unique_interer)
/* any field declarations go here */
int foo;
EXCEPTION_CTOR (type)
/* exception constructor, arguments avalaible as va_list, 'this' pointer avalaible as EXCEPTION */
EXCEPTION->foo = va_arg (EXCEPTION_ARG, int);
EXCEPTION_DTOR (type)
/* exception destructor */
EXCEPTION_DONE
5.2) REGISTERING EXCEPTIONS
Call this function olny after you've called EXCEPTION_INIT
REGISTER_EXCEPTION (type);
5.3) THROWING EXCEPTIONS
There are two functions: throw and vthrow. Both take type of exception as it's first argument.
throw (type, arg1, arg2, ...);
vthrow (type, argumenst_as_va_list);
5.4) CATCHING EXCEPTIONS
Minimal version, try to do something dangerous:
try {
/* dangerous code */
} finally {
/* cleanup code */
} untry;
More complicated version, catch any exceptions thrown:
try {
/* dangerous code */
} catch_rest {
/* code executed if any exception was thrown */
} finally {
/* cleanup code */
} untry;
Even more compliacted: catch specific exceptions:
try {
dangerous code
} catch (type) {
/* code executed when exception TYPE was thrown,
data avalaible as EXCEPTION (pointer to struct) */
prinf ("foo: %d\n", EXCEPTION->some_field);
} finally {
/* cleanup code */
} untry;
Full version:
try {
/* dangerous code */
} catch (type) {
/* code executed when exception TYPE was thrown,
data avalaible as EXCEPTION (pointer to struct) */
prinf ("foo: %d\n", EXCEPTION->some_field);
} catch_rest {
/* code executed if exception was thrown
but could not be handled by any specific catch */
} finally {
/* cleanup code */
} untry;
EXAMPLE: (cut here)
#include <stdio.h>
#include <stdlib.h>
#define LOWERCASE_EXCEPTIONS
#include "exception.h"
enum MY_EXCEPTIONS { EXCEPTION_SOME = 1, EXCEPTION_OTHER };
/* -------------- */
/* exception definitions no semicolons after macros! */
EXCEPTION_DEFINE (some, EXCEPTION_SOME)
int foo;
EXCEPTION_CTOR (some)
EXCEPTION->foo = va_arg (EXCEPTION_ARG, int);
printf ("some: ctor, foo: %d\n", EXCEPTION->foo);
EXCEPTION_DTOR (some)
printf ("some: dtor\n");
EXCEPTION_DONE
/* ----------- */
EXCEPTION_DEFINE (other, EXCEPTION_OTHER)
EXCEPTION_CTOR (other)
printf ("other: ctor\n");
EXCEPTION_DTOR (other)
printf ("other: dtor\n");
EXCEPTION_DONE
/* ----------- */
void danger()
{
printf ("danger: entering\n");
throw (some, 10);
printf ("danger: exiting\n");
}
void inner()
{
char *buf;
printf("inner: entering\n");
buf = malloc(100);
try {
danger();
} finally {
printf ("inner: cleanup\n");
free (buf);
} untry;
printf ("inner: exiting\n");
}
int main(void)
{
EXCEPTION_INIT (malloc(1024), 1024); /* exception stack */
REGISTER_EXCEPTION (some);
REGISTER_EXCEPTION (other); /* registering exceptions */
try {
printf ("main: try block\n");
try {
printf ("main, nested: try block\n");
throw (other);
} catch (other) {
printf ("main, nested: cought other\n");
} finally {
printf ("main, nested cleanup\n");
} untry;
printf ("main: again in try block\n");
inner();
} catch (some) {
printf ("main: cought some, foo: %d\n", EXCEPTION->foo);
} catch (other) {
printf ("main: cough other\n");
} catch_rest {
printf ("main: cought unknown exception\n");
} finally {
printf ("main: cleanup\n");
} untry;
return EXIT_SUCCESS;
}
OUTPUT FROM THE EXAMPLE:
main: try block
main, nested: try block
other: ctor
main, nested: cought other
main, nested cleanup
other: dtor
main: again in try block
inner: entering
danger: entering
some: ctor, foo: 10
inner: cleanup
main: cought some, foo: 10
main: cleanup
some: dtor