TRY Language Extension

error.h

Starts a protected block of code.

TRY, ONERR, ENDTRY, FINALLY, ENDFINAL, and PASS are macros which extend the C language to implement an error handling mechanism which is almost identical to the commonly-used exception handling as well as error handling in TI-Basic.

TRY begins the protected block. It is a macro which is implemented using the ER_catch function. If an error occurs in the protected block, program execution transfers to the block after the ONERR or FINALLY statement.

However, ONERR and FINALLY are entirely different. The ONERR block will not be executed if there was no error, and it is assumed to either handle the error or call PASS. The error is cleared, so if PASS is not called, the execution continues normally after the ENDTRY statement.

FINALLY, on the other hand, is not implemented to handle errors, but instead to create a block of code which will always be executed, regardless of whether there was an error or not. It does not clear the error (or more precisely, ENDFINAL throws it again). This can be used in a function which allocates memory, destroys the contents of the screen, or does something else which always needs cleaning up. If the function throws an error in a protected block, the memory should always be freed again, the screen should be restored, etc. But the error should not be handled at the same time; instead, it must be handled on a higher level (often even in the calling function).

The usage of ONERR is illustrated in the following example:

TRY
  // <protected code>
ONERR
  if (errCode == some_specific_code)
    // <error handler>
  else
    // pass on any unhandled errors to a higher level
    PASS;
ENDTRY
The usage of FINALLY is illustrated in the following example:
TRY
  ...
  // <allocate memory>
  TRY
    // <protected code>
  FINALLY
    // <free the allocated memory>
  ENDFINAL
  ...
ONERR
  // <error handler>
ENDTRY
The variable errCode is automatically created in the error handler, and it contains the error number to allow the program to check what caused the error. This variable will be destroyed after the ENDTRY/ENDFINAL statement.

It is important to say that you must not exit the protected block using goto or return statements, else the error frame will not be removed, so the further behavior of the program will be unpredictable. If you really want to exit from the protected block before its natural end (i.e. before the ONERR or FINALLY statement), call ER_success explicitely to remove the error frame before exiting, i.e. do something like
TRY
  ...
  if (I_really_must_exit_from_here)
  {
    ER_success ();
    return;
  }
  ...
ONERR
  ...
ENDTRY
But in general this is a very bad practice and should be avoided even if it requires some extra code. For example, you can rewrite the code like this:
TRY
  ...
  if (!I_really_must_exit_from_here)
  {
    ...
  }
ONERR
  ...
ENDTRY
if (I_really_must_exit_from_here)
  return;
There is also another possible caveat related to error handling. The TRY macro (or ER_catch, more precisely) saves many of the registers on its execution context stack, since ER_catch needs to simulate a return identical to the return of any normal function. Consequently, when an error is thrown, all variables which reside in registers are reset to their contents before the TRY macro was called. If code in an ONERR or FINALLY block needs the value of a variable set in the TRY block, the code must arrange to make sure the C code optimizer does not put that variable in a register. This can be accomplished by declaring such variables to be volatile. So, remember this rule: Variables changed in a TRY block must be declared volatile if they are referenced in an ONERR or FINALLY block!

If you want to protect the whole program, passing all unhandled errors to the operating system, you can define ENABLE_ERROR_RETURN instead of using a TRY...ONERR...ENDTRY block. See the section Returning Errors for more information.


Uses: ER_catch, errCode