try <block> catch e <block> end tryIn addition the builtin routine throw() can be used to transfer control directly to the catch block, potentially from within several nested routine calls.
The catch statement traps an exception within the try block, or any routines it invokes, that is not first caught by another nested try/catch statement.
Any exceptions that must be re-thrown must be done so explicitly, and unlike other languages there is only one (non-optional) catch clause.
There are no confusing classes of exceptions, and no implicit filtering, and hence no unexpected leaks to the outside world.
The exception variable is declared immediately after the catch keyword and is a sequence. It can be given with any valid identifier as a name, though "e" is usually sufficient.
Just like the loop variable of a for statement, it can be predeclared and then persists after the end try, otherwise it is automatically declared and drops out of scope on the end try.
For details of the contents of the exception variable, see throw().
Comments
Note that a try/catch statement may interfere with your ability to debug code within the try block (although trace and printf are still fine).It can certainly negate the idea of a "fail fast" development methodology, should the program quietly soldier on rather than immediately alert you to a problem and force you to fix it.
The authors of the Go programming language (and certainly neither Rob Pike nor Ken Thompson can be considered amateurs) at first completely omitted exceptions, but eventually (after some probably quite relentless pressure) added a panic/recover mechanism; clearly they had their reservations, so perhaps you should too.
There is no guarantee that all exceptions can be caught: rogue code that corrupts the stack will make it impossible to even look for a suitable handler.
The phix abort() and crash() routines now invoke throw() when an exception handler is detected, however the equivalent of the former in non-phix code (ie kernel32.dll/ExitProcess or sys_exit from a dll/so or inline assembly) cannot be caught (by any means that I know or care of). Also, passing invalid arguments to throw() (e not atom when user_data not {}, or wrong-type-elements) is deemed to be non-catchable. Apart from that I am not currently aware of any errors, or any types or classes of error, that can never be caught.
(In at least one release a try statement even had the power to prevent you from quitting a debug/trace session; it no longer re-throws e12pa, ie '!' keyed.)
There is no type checking on a catch clause, since throw() always provides a sequence, hence user defined types are not permitted on predeclared exception variables.
The catch clause is actually opTryend (ie restore the previous exception handler, if any), then a jump to the end try, and then the actual catch handler itself (which also restores the previous exception handler, if any). Leaving a blank (or comment-only) line before the catch keyword may make some listing files (-d command line option) slightly easier to follow.
Compilation issues the error "invalid (circumvents try handler reset)" when appropriate. The try block may not contain any exit/break/continue statements that would circumvent the reset of the handler. In contrast, return statements are perfectly valid, as are any exit/break/continue statements within the catch block, or wholly nested within (/not leaking from) the try block.
- aside:
- However, the use of #ilASM{} to effect a jump with said circumvention is not protected against, but it will probably go horribly wrong, should anything untoward happen, especially before a return discards the handler naturally, or a containing try effects a higher-level handler reset. The system stack is rebalanced along with the exception hander reset, mainly in case something happened deep inside a c_func/proc/call_back, and hence jumping to the "wrong" handler may very well unbalance the system stack. (The triggering of a higher level catch may help.)
Compilation issues "Warning: empty catch block" messages when appropriate. A blanket "ignore all" handler in the top-level main loop of an application may sound like an easy way to make your code "robust". However it also risks making you completely blind towards any emerging problems - which can in some cases be perfectly fine - if a "jumping giraffe" game tries to draw the eyeballs off-screen, no-one cares, but a bug in the phix compiler is quite different. At the very least you should consider something like
if DEBUG then ?9/0 end if
and in that way your live customers
can experience the trouble-free life you think they deserve, while your development staff are kept honest and immediately informed
of any developing issues, and forced to address them. Be warned that "trouble-free life" may turn into the classic headbutting a brick wall experience of no clue as to what is going wrong - a log file of quietly ignored exceptions may help.
I have four recommendations (which may overlap and overpreach a bit):
- Write the code without a try container and see how far you get. Typically something like a subscript error can and should be fixed immediately, with the try statement being intended to guard against something completely different. If not careful, a try statement will mask errors that you might much rather it did not.
- Always try and localise a try statement as much as possible, to cover the least amount of code and solve a very specific
problem. Your catch clauses will obviously be much easier to write.
Admittedly half the point of a catch clause is to "common up" error handling, but don’t go overboard. - Use try statements to deal with third-party code that you cannot or would rather not modify, as opposed to permission
for your own sloppily-written abomination™.
A perfect example of the former: display "unable to play video" rather than let a corrupt file euthanise your entire application. Obviously the compiler simply cannot distinguish between "blimey, who’d’ve thunk that could ever happen" from "stupid human does not know first thing about writing code", and must therefore treat them identically. - Start a new catch clause as
?e
, then gradually add conditions and filters, with comments, for specific cases where a "do nothing" response is genuinely the best thing to do.
Let everything you have not yet seen show up, including that bug you have just introduced, as well as the bug you probably will introduce sometime next week.