When an error occurs (and you want to handle it), you raise an exception using either the
Raise with a number which identifies the kind of error that occurred.
The code in the exception handler is responsible for decoding the number and then doing the appropriate thing.
Throw is very similar to
Raise, and the following description of
Raise also applies to
The difference is that
Throw takes a second argument which can be used to pass extra information to a handler (usually a string).
The terms `raising' and `throwing' an exception can be used interchangeably.
Raise is called it immediately stops the execution of the current procedure code and passes control to the exception handler of most recent procedure which has a handler (which may be the current procedure).
This is a bit complicated, but you can stick to raising exceptions and handling them in the same procedure, as in the next example:
CONST BIG_AMOUNT = 100000 ENUM ERR_MEM=1 PROC main() HANDLE DEF block block:=New(BIG_AMOUNT) IF block=NIL THEN Raise(ERR_MEM) WriteF('Got enough memory\n') EXCEPT IF exception=ERR_MEM WriteF('Not enough memory\n') ELSE WriteF('Unknown exception\n') ENDIF ENDPROC
This uses an exception handler to print a message saying there wasn't enough memory if the call to
The parameter to
Raise is stored in the special variable
exception in the exception handler part of the code, so if
Raise is called with a number other than
ERR_MEM a message saying "Unknown exception" will be printed.
Try running this program with a really large
BIG_AMOUNT constant, so that the
New can't allocate the memory.
Notice that the "Got enough memory" is not printed if
Raise is called.
That's because the execution of the normal procedure code stops when
Raise is called, and control passes to the appropriate exception handler.
When the end of the exception handler is reached the procedure is finished, and in this case the program terminates because the procedure was the
Throw is used instead of
Raise then, in the handler, the special variable
exceptioninfo will contain the value of the second parameter.
This can be used in conjunction with
exception to provide the handler with more information about the error.
Here's the above example re-written to use
CONST BIG_AMOUNT = 100000 ENUM ERR_MEM=1 PROC main() HANDLE DEF block block:=New(BIG_AMOUNT) IF block=NIL THEN Throw(ERR_MEM, 'Not enough memory\n') WriteF('Got enough memory\n') EXCEPT IF exception=ERR_MEM WriteF(exceptioninfo) ELSE WriteF('Unknown exception\n') ENDIF ENDPROC
An enumeration (using
ENUM) is a good way of getting different constants for various exceptions.
It's always a good idea to use constants for the parameter to
Raise and in the exception handler, because it makes everything a lot more readable:
Raise(ERR_MEM) is much clearer than
The enumeration starts at one because zero is a special exception: it usually means that no error occurred.
This is useful when the handler does the same cleaning up that would normally be done when the program terminates successfully.
For this reason there is a special form of
EXCEPT which automatically raises a zero exception when the code in the procedure successfully terminates.
EXCEPT DO, with the
DO suggesting to the reader that the exception handler is called even if no error occurs.
Also, the argument to the
Raise function defaults to zero if it is omitted (see 7.3 Default Arguments).
So, what happens if you call
Raise in a procedure without an exception handler?
Well, this is where the real power of the handling mechanism comes to light.
In this case, control passes to the exception handler of the most recent procedure with a handler.
If none are found then the program terminates.
`Recent' means one of the procedures involved in calling your procedure.
So, if the procedure
barney, then when
barney is being executed
fred is a recent procedure.
main procedure is where the program starts it is a recent procedure for every other procedure in the program.
This means, in practice:
fredto be a procedure with an exception handler then any procedures called by
fredwill have their exceptions handled by the handler in
fredif they don't have their own handler.
mainto be a procedure with an exception handler then any exceptions that are raised will always be dealt with by some exception handling code (i.e., the handler of
mainor some other procedure).
Here's a more complicated example:
ENUM FRED=1, BARNEY PROC main() WriteF('Hello from main\n') fred() barney() WriteF('Goodbye from main\n') ENDPROC PROC fred() HANDLE WriteF(' Hello from fred\n') Raise(FRED) WriteF(' Goodbye from fred\n') EXCEPT WriteF(' Handler fred: \d\n', exception) ENDPROC PROC barney() WriteF(' Hello from barney\n') Raise(BARNEY) WriteF(' Goodbye from barney\n') ENDPROC
When you run this program you get the following output:
Hello from main Hello from fred Handler fred: 1 Hello from barney
This is because the
fred procedure is terminated by the
Raise(FRED) call, and the whole program is terminated by the
Raise(BARNEY) call (since
main do not have handlers).
Now try this:
ENUM FRED=1, BARNEY PROC main() WriteF('Hello from main\n') fred() WriteF('Goodbye from main\n') ENDPROC PROC fred() HANDLE WriteF(' Hello from fred\n') barney() Raise(FRED) WriteF(' Goodbye from fred\n') EXCEPT WriteF(' Handler fred: \d\n', exception) ENDPROC PROC barney() WriteF(' Hello from barney\n') Raise(BARNEY) WriteF(' Goodbye from barney\n') ENDPROC
When you run this you get the following output:
Hello from main Hello from fred Hello from barney Handler fred: 2 Goodbye from main
fred procedure calls
fred are recent procedures when
Raise(BARNEY) is executed, and therefore the
fred exception handler is called.
When this handler finishes the call to
main is finished, so the
main procedure is completed and we see the `Goodbye' message.
In the previous program the
Raise(BARNEY) call did not get handled and the whole program terminated at that point.
Go to the Next or Previous section, the Detailed Contents, or the Amiga E Encyclopedia.