(cross from alt.comp.lang.c) Experimental exception handliong in C

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
 
Z

Zygmunt Krynicki

On Wed, 17 Sep 2003 12:51:34 +0000, Zygmunt Krynicki wrote:

[snap]

Being curious: have I voilated some critical usnet rule or was my post
plain boring? I would really like any response, criticism or whatever.

Thanks
Z.
 
N

Nils Petter Vaskinn


don't you mean [snip] ?
Being curious: have I voilated some critical usnet rule or was my post
plain boring? I would really like any response, criticism or whatever.

I think the mainly people aren't interested. If we want exception handling
most people will use a language that has exceptions instead of trying to
put them on top of C.

Obviously I speak only for myself, but if noone contradicts me ...
 
L

LibraryUser

Zygmunt said:
On Wed, 17 Sep 2003 12:51:34 +0000, Zygmunt Krynicki wrote:

[snap]

Being curious: have I voilated some critical usnet rule or was
my post plain boring? I would really like any response,
criticism or whatever.

It is probably Off-Topic, since it refers to a specific
compiler/system, and I doubt very much that it is written in pure
standard and portable C. At any rate it did not include the
actual code (which may well be too long for a usenet article
anyway), thus inhibiting any examination. What you did post did
not even include full prototypes of the functions.
 
Z

Zygmunt Krynicki


don't you mean [snip] ?

Yes, thank you.
I think the mainly people aren't interested. If we want exception handling
most people will use a language that has exceptions instead of trying to
put them on top of C.

True, and if I had a choice now I would probably start the whole thing
from scratch and use C++ the point is that I cannot as I got 99% of the
code up and running and the part that is using exceptions is just a small
fraction although deeply integrated so it would be difficult to separate
it from the rest).

Thank you for your answer.

Z.
 
Z

Zygmunt Krynicki

It is probably Off-Topic, since it refers to a specific
compiler/system, and I doubt very much that it is written in pure

It is perfectly portable, written in standard C, the extensions I've used
are mearly a minor addiction and could be removed without any difference
for the final code. I used them to inform my compiler that specific
functions will never return so that the code could be generated better.

Did you even read the whole message? The readme file I've included was
clearly stating that this is written in pure portable C.
standard and portable C. At any rate it did not include the
actual code (which may well be too long for a usenet article

the code as you might have guessed is not included in the message because
it's too long, If you have read the message carefully you would have
noticed that at the very beginning I give the location of the full source
(freely avalaible at http://lexx.eu.org/~zyga/exceptions.tar.gz)
anyway), thus inhibiting any examination. What you did post did
not even include full prototypes of the functions.

Full prototypes are declared in the header file "exception.h" which is
#included in the example I've sent. I didn't want to throw a bunch of
cryptic code and ask "well, how do you like it?" I wanted to show the
possible usage of my library, in other words: to show it from the nice
side. Anyone interested in the details then could look at the sources to
learn more.


In any case: that library is now old, I've got alot better one avalaible
(I've posted in some samples in another thread) and probably debugged
fully. If you are interested feel free to email me and I'll send you the
source code.

Regards

Zygmunt
 
T

those who know me have no need of my name

in comp.lang.c i read:
I've also added http://lexx.eu.org/~zyga/exceptions-new.tar.gz which are
alot better from the previous ones.

it doesn't even compile (even after editing to remove the blatant gcc-isms).

| $ make
| Make: line 20: syntax error. Stop.
|
| $ cc -c exception.c
| cc: "exception.c", line 11: error 1539: Cannot do arithmetic with pointers to objects of unknown size.

also, i should point out that identifiers which begin with two underscores
are reserved.

your test wrapper fared no better.

| $ cc -c test.c
| cc: "test.c", line 14: error 1000: Unexpected symbol: "}".
| cc: "test.c", line 18: warning 23: Semicolon required after last member.
| cc: "test.c", line 18: warning 500: Unnamed struct/union members are ignored.
| cc: "test.c", line 12: error 1613: Zero-sized struct.
| cc: "test.c", line 73: warning 504: The sizeof operator applied to a zero-sized object.
| cc: "test.c", line 87: warning 611: Type conversion loses "const" qualifier.
| cc: "test.c", line 101: error 1634: Missing arguments only allowed on intrinsic calls.
| cc: "test.c", line 112: warning 611: Type conversion loses "const" qualifier.
 
Z

Zygmunt Krynicki

it doesn't even compile (even after editing to remove the blatant gcc-isms).

| $ make
| Make: line 20: syntax error. Stop.
|
| $ cc -c exception.c
| cc: "exception.c", line 11: error 1539: Cannot do arithmetic with pointers to objects of unknown size.

Fixed, the line should read: __egd_s = __egd_b = (char*)stack + size;
also, i should point out that identifiers which begin with two underscores
are reserved.

Yes I know. I'll get rid of "__" later on but since it doesn't seem to be
causing trouble it's not top priority right now.
your test wrapper fared no better.

| $ cc -c test.c
| cc: "test.c", line 14: error 1000: Unexpected symbol: "}".
| cc: "test.c", line 18: warning 23: Semicolon required after last member.
| cc: "test.c", line 18: warning 500: Unnamed struct/union members are ignored.
| cc: "test.c", line 12: error 1613: Zero-sized struct.
| cc: "test.c", line 73: warning 504: The sizeof operator applied to a zero-sized object.
| cc: "test.c", line 87: warning 611: Type conversion loses "const" qualifier.
| cc: "test.c", line 101: error 1634: Missing arguments only allowed on intrinsic calls.
| cc: "test.c", line 112: warning 611: Type conversion loses "const" qualifier.

Are empty structures illegal in C?
What compiler were you using? I'd like to fix those errors.

The error on line 101 is concerned with expanding a variadic macro without
the extra parameters. A simple workaround is to add a dummy int to the
structure and pass some value along (or use the function call directly
bypassing the macro mechanism of your compiler.

Regards

Z.
 
T

those who know me have no need of my name

in comp.lang.c i read:
Fixed, the line should read: __egd_s = __egd_b = (char*)stack + size;
Are empty structures illegal in C?

yes; at least one member is required.
What compiler were you using? I'd like to fix those errors.

several, most recently:

| $ cc -V
| Compaq C V6.5-011 on Compaq Tru64 UNIX V5.1B (Rev. 2650)
| Compiler Driver V6.5-003 (sys) cc Driver

making the corrections you suggested results in a link failure, there is no
function in standard c called vasprintf. eliminating the vasprintf
(whether that is appropriate or not i did not analyze) yields an
executable, but 3 of the 4 test cases have problems, e.g., ...

| $ ./a.out
| Enter number [1-4]: 1
| Unaligned access pid=1984303 <a.out> va=0x11fffbff4 pc=0x120001cb8 ra=0x120001c9c inst=0xb5200000
| Unaligned access pid=1984303 <a.out> va=0x11fffbff4 pc=0x120002210 ra=0x1200021f4 inst=0xa4210000
| caught foo: 10
| cleanup
| Unaligned access pid=1984303 <a.out> va=0x11fffbff4 pc=0x120001d98 ra=0x140006030 inst=0xa7430000

on some platforms unaligned access causes a crash instead of a warning.
 
Z

Zygmunt Krynicki

yes; at least one member is required.

That explains much, thank you.

| $ ./a.out
| Enter number [1-4]: 1
| Unaligned access pid=1984303 <a.out> va=0x11fffbff4 pc=0x120001cb8 ra=0x120001c9c inst=0xb5200000
| Unaligned access pid=1984303 <a.out> va=0x11fffbff4 pc=0x120002210 ra=0x1200021f4 inst=0xa4210000
| caught foo: 10
| cleanup
| Unaligned access pid=1984303 <a.out> va=0x11fffbff4 pc=0x120001d98 ra=0x140006030 inst=0xa7430000

on some platforms unaligned access causes a crash instead of a warning.

I did my own test on a couple of Alphas at the university and ran into the
same problem as you did. The solution is obvious (at least I think it is,
which might be tricky ;-) but will hurt performance a little. Instead of
passing a pointer to an unaligned memory and interpret it as a structure.
I'll pass the pointer to/from which each constructor/destructor would have
to copy it's data. I'll send an update as soon as I get it working.

Thank you for your contribution :)

Z.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top