Exceptions/Catch an exception thrown in a nested call: Difference between revisions
Content added Content deleted
m (→{{header|Lasso}}: Corrected output) |
|||
Line 303: | Line 303: | ||
=={{header|C}}== |
=={{header|C}}== |
||
{{incomplete|C|Whot! No bar() ?}} |
|||
C doesn't have exception handling |
C doesn't have an exception handling mechanism, so we have to |
||
decide what we want from an exception. |
|||
1. Return from a function with an error added to exception context. |
|||
2. Detect that a exception was thrown by checking the context after a function call. |
|||
3. Recover an error type and message. |
|||
4. Return from a function after throwing/catching an exception in a way which allows cleanup code to be called (vs. jumping outside the function). |
|||
In conclusion, try/throw/catch keywords are not available in C, nor is |
|||
their functionality, so while the following code tries to fulfill the task's |
|||
requirements, no attempt is made to minic them. The goal has been to provide |
|||
some modicum level of usefulness for someone actually looking at this for |
|||
ideas for their own code. |
|||
U0 and U1 are boring for debugging purposes. Added something to help with that. |
|||
<lang C>#include <stdio.h> |
<lang C>#include <stdio.h> |
||
#include <stdlib.h> |
#include <stdlib.h> |
||
#include <string.h> |
|||
#include <ucontext.h> |
|||
typedef struct exception { |
|||
#define try push_handler(); if (!exc_string) |
|||
int extype; |
|||
#define catch(e) pop_handler(e); for (; exc_string; exc_string = 0) |
|||
char what[128]; |
|||
} exception; |
|||
typedef struct exception_ctx { |
|||
ucontext_t *exc; |
|||
exception * exs; |
|||
int exc_depth = 0; |
|||
int size; |
|||
int exc_alloc = 0; |
|||
int pos; |
|||
const char * exc_string; |
|||
} exception_ctx; |
|||
exception_ctx * Create_Ex_Ctx(int length) { |
|||
void throw(const char *str) |
|||
const int safety = 8; // alignment precaution. |
|||
{ |
|||
char * tmp = (char*) malloc(safety+sizeof(exception_ctx)+sizeof(exception)*length); |
|||
exc_string = str; |
|||
if (! tmp) return NULL; |
|||
exception_ctx * ctx = (exception_ctx*)tmp; |
|||
ctx->size = length; |
|||
ctx->pos = -1; |
|||
ctx->exs = (exception*) (tmp + sizeof(exception_ctx)); |
|||
return ctx; |
|||
} |
} |
||
void Free_Ex_Ctx(exception_ctx * ctx) { |
|||
void push_handler() |
|||
free(ctx); |
|||
{ |
|||
exc_string = 0; |
|||
if (exc_alloc <= exc_depth) { |
|||
exc_alloc += 16; |
|||
exc = realloc(exc, sizeof(ucontext_t) * exc_alloc); |
|||
} |
|||
getcontext(exc + exc_depth++); |
|||
} |
} |
||
int Has_Ex(exception_ctx * ctx) { |
|||
void pop_handler(const char *e) |
|||
return (ctx->pos >= 0) ? 1 : 0; |
|||
{ |
|||
exc_depth --; |
|||
if (exc_string && strcmp(e, exc_string)) { |
|||
if (exc_depth > 0) |
|||
throw(exc_string); |
|||
fprintf(stderr, "Fatal: unhandled exception %s\n", exc_string); |
|||
exit(1); |
|||
} |
|||
} |
} |
||
int Is_Ex_Type(exception_ctx * exctx, int extype) { |
|||
/* try out the exception system */ |
|||
return (exctx->pos >= 0 && exctx->exs[exctx->pos].extype == extype) ? 1 : 0; |
|||
} |
|||
void Pop_Ex(exception_ctx * ctx) { |
|||
if (ctx->pos >= 0) --ctx->pos; |
|||
} |
|||
const char * Get_What(exception_ctx * ctx) { |
|||
if (ctx->pos >= 0) return ctx->exs[ctx->pos].what; |
|||
return NULL; |
|||
} |
|||
int Push_Ex(exception_ctx * exctx, int extype, const char * msg) { |
|||
void baz() { |
|||
if (++exctx->pos == exctx->size) { |
|||
// Use last slot and report error. |
|||
switch (count++) { |
|||
--exctx->pos; |
|||
fprintf(stderr, "*** Error: Overflow in exception context.\n"); |
|||
case 2: throw("U2"); |
|||
} |
} |
||
snprintf(exctx->exs[exctx->pos].what, sizeof(exctx->exs[0].what), "%s", msg); |
|||
exctx->exs[exctx->pos].extype = extype; |
|||
return -1; |
|||
} |
} |
||
////////////////////////////////////////////////////////////////////// |
|||
void foo() |
|||
{ |
|||
exception_ctx * GLOBALEX = NULL; |
|||
printf(" foo: calling baz\n"); |
|||
enum { U0_DRINK_ERROR = 10, U1_ANGRYBARTENDER_ERROR }; |
|||
try { baz(); } |
|||
catch("U0") { |
|||
void baz(int n) { |
|||
printf(" foo: got exception U0; handled and dandy\n"); |
|||
if (! n) { |
|||
Push_Ex(GLOBALEX, U0_DRINK_ERROR , "Drink Error. Insufficient drinks in bar Baz."); |
|||
return; |
|||
} |
} |
||
else { |
|||
Push_Ex(GLOBALEX, U1_ANGRYBARTENDER_ERROR , "Bartender Error. Bartender kicked customer out of bar Baz."); |
|||
return; |
|||
} |
|||
} |
|||
void bar(int n) { |
|||
printf(" foo: finished\n"); |
|||
fprintf(stdout, "Bar door is open.\n"); |
|||
baz(n); |
|||
if (Has_Ex(GLOBALEX)) goto bar_cleanup; |
|||
fprintf(stdout, "Baz has been called without errors.\n"); |
|||
bar_cleanup: |
|||
fprintf(stdout, "Bar door is closed.\n"); |
|||
} |
} |
||
void foo() { |
|||
fprintf(stdout, "Foo entering bar.\n"); |
|||
bar(0); |
|||
while (Is_Ex_Type(GLOBALEX, U0_DRINK_ERROR)) { |
|||
printf("main: calling foo: %d\n", i); |
|||
fprintf(stderr, "I am foo() and I deaall wrth U0 DriNk Errors with my own bottle... GOT oNE! [%s]\n", Get_What(GLOBALEX)); |
|||
try { foo(); } |
|||
Pop_Ex(GLOBALEX); |
|||
printf("main: Someone threw U1; handled and dandy\n"); |
|||
} |
|||
} |
} |
||
if (Has_Ex(GLOBALEX)) return; |
|||
fprintf(stdout, "Foo left the bar.\n"); |
|||
fprintf(stdout, "Foo entering bar again.\n"); |
|||
bar(1); |
|||
while (Is_Ex_Type(GLOBALEX, U0_DRINK_ERROR)) { |
|||
fprintf(stderr, "I am foo() and I deaall wrth U0 DriNk Errors with my own bottle... GOT oNE! [%s]\n", Get_What(GLOBALEX)); |
|||
Pop_Ex(GLOBALEX); |
|||
} |
|||
if (Has_Ex(GLOBALEX)) return; |
|||
fprintf(stdout, "Foo left the bar.\n"); |
|||
} |
|||
int main(int argc, char ** argv) { |
|||
exception_ctx * ctx = Create_Ex_Ctx(5); |
|||
GLOBALEX = ctx; |
|||
foo(); |
|||
if (Has_Ex(ctx)) goto main_ex; |
|||
fprintf(stdout, "No errors encountered.\n"); |
|||
main_ex: |
|||
while (Has_Ex(ctx)) { |
|||
fprintf(stderr, "*** Error: %s\n", Get_What(ctx)); |
|||
Pop_Ex(ctx); |
|||
} |
|||
Free_Ex_Ctx(ctx); |
|||
return 0; |
return 0; |
||
} |
|||
}</lang>Output:<lang>main: calling foo: 0 |
|||
foo: calling baz |
|||
foo: got exception U0; handled and dandy |
|||
</lang> |
|||
foo: finished |
|||
Output: |
|||
main: calling foo: 1 |
|||
<lang>Foo entering bar. |
|||
foo: calling baz |
|||
Bar door is open. |
|||
main: Someone threw U1; handled and dandy |
|||
Bar door is closed. |
|||
main: calling foo: 2 |
|||
I am foo() and I deaall wrth U0 DriNk Errors with my own bottle... GOT oNE! [Drink Error. Insufficient drinks in bar Baz.] |
|||
foo: calling baz |
|||
Foo left the bar. |
|||
Unhandled exception U2</lang> |
|||
Foo entering bar again. |
|||
Disclaimer: this is pure hackery, and any kind of bad thing could happen with it. You are not seriously going to use it. |
|||
Bar door is open. |
|||
Bar door is closed. |
|||
*** Error: Bartender Error. Bartender kicked customer out of bar Baz. |
|||
</lang> |
|||
=={{header|C++}}== |
=={{header|C++}}== |