TRY / CATCH |
|
|
The TRY/CATCH construct allows creation of exception handlers in a manner broadly similar to that of some other programming languages.
Format
TRY {statement} {statements} CATCH exception {qualifiers}{, exception {qualifiers}...} {statements} CATCH exception {qualifiers}{, exception {qualifiers}...} {statements} END
where
A single TRY construct may have multiple CATCH clauses.
An exception is a named event, typically an error, raised within an application by use of the THROW statement. The actual name has no significance to QM but should ideally relate to the situation that the exception handles. Exception names are case insensitive and may be up to 63 characters.
A TRY/CATCH construct executes the statement(s) in the TRY clause, monitoring for named exceptions being thrown.
On throwing an exception, the application exits from all lower level programs, subroutines, etc and continues execution in the CATCH clause that handles this exception. Where an object oriented programming object is discarded by an exception, the DESTROY.OBJECT subroutine will be executed in the normal way.
Whilst execution of subroutines that return is acceptable within the TRY clause, it is essential that programs do not jump into or out of this clause. Failure to follow this rule may have undesirable results.
A single CATCH clause may catch multiple exception names. On arrival in the CATCH clause, the @EXCEPTION variable will contain the name of the exception that has been caught and the @EXCEPTION.DATA variable will contain any data associated with this exception. The @EXCEPTION.ORIGIN variable is set to a dynamic array in which field 1 holds the program name from which the exception was thrown and field 2 holds the line number in that program. If the program has no cross-reference tables, the line number will be -1.
If the exception name in the CATCH clause is followed by the DUMPING or SAVING.STACK options, the additional diagnostic data is generated when the exception is thrown, before unwinding the call stack to the exception handler.
Examples
LOOP READNEXT ACC.ID ELSE EXIT TRY CALL PROCESS.ACCOUNT CATCH ACCOUNT.INVALID DISPLAY 'Account ' : ACC.ID : ' is invalid' END REPEAT
The above rather simple example shows a program fragment that processes successive items from a select list. If the PROCESS.ACCOUNT subroutine, or any lower level action called from it, throws an ACCOUNT.INVALID exception, the program will continue execution at the DISPLAY statement.
TRY CALL MYSUB CATCH TERMINATE DUMPING DISPLAY 'Process status dumped' END
The above example establishes an exception handler around a call to MYSUB. If the user defined TERMINATE exception occurs, a process dump file will be created prior to arrival in the CATCH clause.
A useful report of the call stack can be produced with the code fragment below executed from the CATCH clause. This is also available as a standard catalogued QMBasic subroutine named !EXC.STACK.
IF @EXCEPTION.STACK # '' THEN DISPLAY ' Program name.................. Addr.. Line' LEVEL = 1 FOR EACH PROG IN @EXCEPTION.STACK DELIMITER @FM NAME = FMT(PROG<1,1>, '30L') DISPLAY FMT(LEVEL, 'R%3 ') : SUBDATA = FIELD(PROG<1>, @VM , 2, *) SUBADDR = VSLICE(SUBDATA, 0, 1) SUBLINE = VSLICE(SUBDATA, 0, 2) LOOP DISPLAY FMT(REMOVE(NAME, MORE.NAME), '30L') : ' ' : DISPLAY DTX(REMOVE(SUBADDR, MORE.ADDR), 6) : ' ' : DISPLAY REMOVE(SUBLINE, MORE.LINE) WHILE MORE.NAME OR MORE.ADDR OR MORE.LINE DISPLAY ' ' : REPEAT
LEVEL += 1 NEXT PROG END
See also: |