P99

◆ P99_TRY

#define P99_TRY
Value:
/* one phase for the try, one for the finally */ \
for (register unsigned p00_pha = 0; p00_pha < 2u; ++p00_pha) \
/* Restrict the first phase to the try */ \
if (!p00_pha) \
P00_BLK_START \
P00_BLK_BEFORE(p00_jmp_push(p00_unwind_top)) \
do
#define P99_UNWIND_PROTECT
Unwind execution from several levels of nesting inside a function.
Definition: p99_block.h:604

Create a block that can catch exceptions.

Remarks
This must be used with either P99_CATCH or P99_FINALLY following immediately after the depending block.

The simplest use of this feature is together with P99_FINALLY

unsigned char*volatile buffer = 0;
buffer = malloc(bignumber);
if (!buffer) P99_THROW(thrd_nomem);
// do something complicated with buffer
favorite_func(buffer);
free(buffer);
}

This will ensure that the buffer allocated in buffer will always be freed, regardless of any error conditions the code encounters. In particular this will work, even if an exception is thrown from below the call to favorite_func.

If no exception occurs, the P99_FINALLY clause is always executed. Execution then continues after the clause, just as for normal code.

If an exception occurs, the P99_FINALLY clause is executed (and in this case the call to free is issued). But afterwards, execution will not continue as normal but instead will jump to the next P99_FINALLY or P99_CATCH block on the call stack.

An alternative way is to use P99_CATCH and to handle different exceptions explicitly.

unsigned char*volatile buffer = 0;
buffer = malloc(bignumber);
if (!buffer) P99_THROW(thrd_nomem);
// do something complicated with buffer
} P99_CATCH(int code) {
switch(code) {
case thrd_nomem: perror("we had an allocation error"); break;
case thrd_timedout: perror("we were timed out"); break;
}
free(buffer);
if (code) P99_RETHROW;
}

The difference here is that we receive the error code through the variable code and we can thus take different action for different exceptional conditions. If it weren't for the P99_RETHROW, the unrolling of the call stack would stop at this point and execution would continue after the catch block. The exception would be considered caught.

Here, since there is a P99_RETHROW, execution will jump to the next P99_FINALLY or P99_CATCH block on the call stack. In fact a catch clause of

P99_CATCH(int code) {
// do something here and then
if (code) P99_RETHROW;
}

Would be equivalent to

// do something here
}

except that this wouldn't give access to code.

Note that the code depending on P99_TRY must always be an entire block with { } surrounding it. The code depending on P99_FINALLY or P99_CATCH doesn't have that restriction and could be just a single statement.

Warning
Utilities that change control flow in an unexpected way may result in the loss of some modifications that are effected on variables. A modern compiler should tell you when you are in such a situation. If it is the case you'd have to declare the variable in question with the volatile qualifier. For an explanation see P99_UNWIND_PROTECT.

Definition at line 579 of file p99_try.h.