**** IMPROVE DOCUMENTATION **** /* 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"); } clean { if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); if (cp1 != NULL) free(cp1); } catch(ex) { printf("cp3=%s", cp3); rethrow; } The code above shows some pitfalls and contains many errors. 01) variable scope Variables which are used in the "clean" or "catch" blocks must be declared before "try", otherwise they only exists inside the "try" block. In the example above, cp1-3 are automatic variables and only exist in the "try" block, the "clean" and "catch" blocks don't know anything about them. 02) variable initialization Variables which are used in the "clean" or "catch" blocks must be initialized before the point of the first possible "throw" is reached. In the example above, "clean" would have trouble using cp3 when mallocex() throws a exception when allocating a TOOBIG buffer. 03) volatile variables Variables which are used in the "clean" or "catch" blocks must be declared volatile, otherwise they might contain outdated information when "throw" does the longjmp(). When using a "free if unset" mechanism like the example does in the "clean" block, the variables must be initialized (see 02) *and* remain valid upon use. 04) clean before catch The "clean" block is evaluated before "catch", so resources being cleaned up must no longer be used in the "catch" block. The example above would have trouble referencing the character strings in the printf() statement because these have been free()d before. 05) variable uninitialization When resources are passed away and out of the scope of the "try/clean/catch" construct and the variables were initialized for using a "free if unset" mechanism then they must be uninitialized after being passed away. The example above would free() cp1 in the "clean" block when mallocex() throws a exception when allocating a TOOBIG buffer. The globalcontext->first pointer hence becomes invalid. /* GOOD EXAMPLE */ | /* ALTERNATE *05* GOOD EXAMPLE */ { /*01*/ | { /*01*/ volatile /*03*/ char *cp1 = NULL /*02*/; | volatile /*03*/ char *cp1 = NULL /*02*/; volatile /*03*/ char *cp2 = NULL /*02*/; | volatile /*03*/ char *cp2 = NULL /*02*/; volatile /*03*/ char *cp3 = NULL /*02*/; | volatile /*03*/ char *cp3 = NULL /*02*/; try { | try { cp1 = mallocex(SMALLAMOUNT); | cp1 = mallocex(SMALLAMOUNT); globalcontext->first = cp1; | globalcontext->first = cp1; cp1 = NULL /*05 give away*/; | /*05 keep responsibility*/ cp2 = mallocex(TOOBIG); | cp2 = mallocex(TOOBIG); cp3 = mallocex(SMALLAMOUNT); | cp3 = mallocex(SMALLAMOUNT); | strcpy(cp1, "foo"); strcpy(cp2, "bar"); | strcpy(cp1, "foo"); strcpy(cp2, "bar"); } | } clean { | clean { /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); | /*04*/ printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3); if (cp3 != NULL) free(cp3); | if (cp3 != NULL) free(cp3); if (cp2 != NULL) free(cp2); | if (cp2 != NULL) free(cp2); /*05 cp1 was given away */ | if (cp1 != NULL) free(cp1); } | } catch(ex) { | catch(ex) { /*05 global context untouched */ | globalcontext->first = NULL; rethrow; } | rethrow; } | } } | }