When an error occurs (and you want to handle it), you raise an exception using either the Raise
or Throw
function.
You call 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 Throw
.
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.
When 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 New
returns NIL
.
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 main
procedure.
If 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 Throw
:
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 Raise(1)
.
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.
This is 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 fred
calls barney
, then when barney
is being executed fred
is a recent procedure.
Because the main
procedure is where the program starts it is a recent procedure for every other procedure in the program.
This means, in practice:
fred
to be a procedure with an exception handler then any procedures called by fred
will have their exceptions handled by the handler in fred
if they don't have their own handler.
main
to 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 main
or 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 barney
and 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
Now the fred
procedure calls barney
, so main
and fred
are recent procedures when Raise(BARNEY)
is executed, and therefore the fred
exception handler is called.
When this handler finishes the call to fred
in 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.