A set of macros providing exception a la C++ in ANSI C (grounding feature). More...
Data Structures | |
struct | xbt_ex_t |
Structure describing an exception. More... | |
Defines | |
#define | TRY |
Introduce a block where exception may be dealed with. | |
#define | TRY_CLEANUP |
optional(!) block for cleanup | |
#define | CATCH(e) |
the block for catching (ie, deal with) an exception | |
#define | _THROW(c, v, m) |
Helper macro for THROWS0-6. | |
#define | THROW0(c, v, m) |
Builds and throws an exception with a string taking no arguments. | |
#define | THROW1(c, v, m, a1) |
Builds and throws an exception with a string taking one argument. | |
#define | THROW2(c, v, m, a1, a2) |
Builds and throws an exception with a string taking two arguments. | |
#define | THROW3(c, v, m, a1, a2, a3) |
Builds and throws an exception with a string taking three arguments. | |
#define | THROW4(c, v, m, a1, a2, a3, a4) |
Builds and throws an exception with a string taking four arguments. | |
#define | THROW5(c, v, m, a1, a2, a3, a4, a5) |
Builds and throws an exception with a string taking five arguments. | |
#define | THROW6(c, v, m, a1, a2, a3, a4, a5, a6) |
Builds and throws an exception with a string taking six arguments. | |
#define | THROW7(c, v, m, a1, a2, a3, a4, a5, a6, a7) |
Builds and throws an exception with a string taking seven arguments. | |
#define | RETHROW |
re-throwing of an already caught exception (ie, pass it to the upper catch block) | |
#define | RETHROW0(msg) |
like THROW0, but adding some details to the message of an existing exception | |
#define | RETHROW1(msg, a) |
like THROW1, but adding some details to the message of an existing exception | |
#define | RETHROW2(msg, a, b) |
like THROW2, but adding some details to the message of an existing exception | |
#define | RETHROW3(msg, a, b, c) |
like THROW3, but adding some details to the message of an existing exception | |
#define | RETHROW4(msg, a, b, c, d) |
like THROW4, but adding some details to the message of an existing exception | |
#define | RETHROW5(msg, a, b, c, d, e) |
like THROW5, but adding some details to the message of an existing exception | |
Enumerations | |
enum | xbt_errcat_t { unknown_error = 0, arg_error, bound_error, mismatch_error, not_found_error, system_error, network_error, timeout_error, thread_error, host_error, tracing_error } |
different kind of errors More... | |
Functions | |
const char * | xbt_ex_catname (xbt_errcat_t cat) |
returns a short name for the given exception category | |
void | xbt_ex_free (xbt_ex_t e) |
Exception destructor. | |
void | xbt_backtrace_display_current (void) |
Shows a backtrace of the current location. | |
void | xbt_backtrace_current (xbt_ex_t *e) |
Captures a backtrace for further use. | |
void | xbt_backtrace_display (xbt_ex_t *e) |
Display a previously captured backtrace. |
A set of macros providing exception a la C++ in ANSI C (grounding feature).
This module is a small ISO-C++ style exception handling library for use in the ISO-C language. It allows you to use the paradigm of throwing and catching exceptions in order to reduce the amount of error handling code without hindering program robustness.
This is achieved by directly transferring exceptional return codes (and the program control flow) from the location where the exception is raised (throw point) to the location where it is handled (catch point) -- usually from a deeply nested sub-routine to a parent routine. All intermediate routines no longer have to make sure that the exceptional return codes from sub-routines are correctly passed back to the parent.
These features are brought to you by a modified version of the libex library, one of the numerous masterpiece of Ralf S. Engelschall.
In SimGrid, an exception is a triple <msg , category , value> where msg is a human-readable text describing the exceptional condition, code an integer describing what went wrong and value providing a sort of sub-category. (this is different in the original libex).
TRY TRIED_BLOCK [TRY_CLEANUP CLEANUP_BLOCK] CATCH (variable) CATCH_BLOCK
This is the primary syntactical construct provided. It is modeled after the ISO-C++ try-catch clause and should sound familiar to most of you.
Any exception thrown directly from the TRIED_BLOCK block or from called subroutines is caught. Cleanups which must be done after this block (whenever an exception arised or not) should be placed into the optionnal CLEANUP_BLOCK. The code dealing with the exceptions when they arise should be placed into the (mandatory) CATCH_BLOCK.
In absence of exception, the control flow goes into the blocks TRIED_BLOCK and CLEANUP_BLOCK (if present); The CATCH_BLOCK block is then ignored.
When an exception is thrown, the control flow goes through the following blocks: TRIED_BLOCK (up to the statement throwing the exception), CLEANUP_BLOCK (if any) and CATCH_BLOCK. The exception is stored in a variable for inspection inside the CATCH_BLOCK. This variable must be declared in the outter scope, but its value is only valid within the CATCH_BLOCK block.
Some notes:
The TRY block is a regular ISO-C language statement block, but
This is because there is some hidden setup and cleanup that needs to be done regardless of whether an exception is caught. Bypassing these steps will break the exception handling facility. The symptom are likely to be a segfault at the next exception raising point, ie far away from the point where you did the mistake. If you suspect that kind of error in your code, have a look at the little script tools/xbt_exception_checker
in the CVS. It extracts all the TRY blocks from a set of C files you give it and display them (and only them) on the standard output. You can then grep for the forbidden keywords on that output.
The CLEANUP and CATCH blocks are regular ISO-C language statement blocks without any restrictions. You are even allowed to throw (and, in the CATCH block, to re-throw) exceptions.
There is one subtle detail you should remember about TRY blocks: Variables used in the CLEANUP or CATCH clauses must be declared with the storage class "volatile", otherwise they might contain outdated information if an exception is thrown.
This is because you usually do not know which commands in the TRY were already successful before the exception was thrown (logically speaking) and because the underlying ISO-C setjmp(3) facility applies those restrictions (technically speaking). As a matter of fact, value changes between the TRY and the THROW may be discarded if you forget the "volatile" keyword.
Exception handling is a very elegant and efficient way of dealing with exceptional situation. Nevertheless it requires additional discipline in programming and there are a few pitfalls one must be aware of. Look the following code which shows some pitfalls and contains many errors (assuming a mallocex() function which throws an exception if malloc(3) fails):
/* BAD_EXAMPLE */ TRY { char *cp1, *cp2, *cp3; cp1 = mallocex(SMALLAMOUNT); globalcontext->first = cp1; cp2 = mallocex(TOOBIG); cp3 = mallocex(SMALLAMOUNT); strcpy(cp1, "foo"); strcpy(cp2, "bar"); } TRY_CLEANUP { if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); if (cp1 != NULL) free(cp1); } CATCH(ex) { printf("cp3=%s", cp3); RETHROW; } /* end_of_bad_example */
This example raises a few issues:
The following is fixed version of the code (annotated with the pitfall items for reference):
/* GOOD_EXAMPLE */ { /*01 */ char *volatile /*03 */ cp1 = NULL /*02 */ ; char *volatile /*03 */ cp2 = NULL /*02 */ ; char *volatile /*03 */ cp3 = NULL /*02 */ ; TRY { cp1 = mallocex(SMALLAMOUNT); global_context->first = cp1; cp1 = NULL /*05 give away */ ; cp2 = mallocex(TOOBIG); cp3 = mallocex(SMALLAMOUNT); strcpy(cp1, "foo"); strcpy(cp2, "bar"); } TRY_CLEANUP { /*04 */ printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3); if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); /*05 cp1 was given away */ } CATCH(ex) { /*05 global context untouched */ RETHROW; } } /* end_of_good_example */
#define _THROW | ( | c, | ||
v, | ||||
m | ||||
) |
Helper macro for THROWS0-6.
c,: | category code (integer) | |
v,: | value (integer) | |
m,: | message text |
If called from within a TRY/CATCH construct, this exception is copied into the CATCH relevant variable program control flow is derouted to the CATCH (after the optional sg_cleanup).
If no TRY/CATCH construct embeds this call, the program calls abort(3).
The THROW can be performed everywhere, including inside TRY, CLEANUP and CATCH blocks.
enum xbt_errcat_t |
different kind of errors
Back to the main Simgrid Documentation page |
Generated for SimGridAPI by
![]() |