Whirlwind Tour of Ada - Part 3c
Exception Handling
Begin/End Blocks
Exception Flow of Control
Declaring and Raising Exceptions
Raise Expressions
Exception Parameters and Getting Exception Names
Introduction
- Review common built-in exceptions
- See how to handle exceptions
- See how to declare exceptions
Common Built-in Exceptions
- Data error: numeric get statement reached a non-number
- End error: get statement reached end of file
- Constraint error: operation applied to invalid value
- Example: array index out of range
- Example: assigning a negative value to a natural number
- Program error - something happened to your program
- Example: open file deleted
Handling Exceptions
- Exception handlers are associated with begin/end blocks
- Any begin/end block can have an exception handler
- Begin/end blocks can be inserted as needed
- Example:
procedure demo_exceptions is
i: natural;
begin
get(i);
-- do something with i
exception
when data_error => put_line("found non-numeric input ");
when end_error => put_line("end of file reached");
when constraint_error => put_line("integer not a natural");
when program_error => put_line("something happened");
end demo_exceptions;
Begin/Exception/End = java try/catch blocks
When Others
- A when others clause can be used to catch any exception
- Example:
procedure demo_exceptions is
i: natural;
begin
get(i);
-- do something with i
exception
when data_error => put_line("found non-numeric input ");
when end_error => put_line("end of file reached");
when constraint_error => put_line("integer not a natural");
when others => put_line("Something happened");
end demo_exceptions;
when others must be the last when clause
Exceptions and Flow of Control
- What happens when an exception E occurs in statement X procedure Y?
It depends on ...
- whether there is an exception handler for X
- and whether that handler handles exception E
- What happens in those cases is best seen by an example
Example: Exception Flow of Control
with ada.integer_text_io; use ada.integer_text_io;
with ada.text_io; use ada.text_io;
procedure tryexceps is
procedure get_one_NO_handler(i: out Natural) is
begin
get(i);
end get_one_NO_handler;
procedure get_one_has_handler(i: out Natural) is
begin
get(i);
exception
when DATA_ERROR =>
put_line("DATA ERROR occurred");
i := 0;
end get_one_has_handler;
procedure get_two_nats(i, j: out Natural) is
begin
get_one_NO_handler(i);
get_one_has_handler(j);
exception
when END_ERROR =>
put_line("END ERROR occurred");
i := 1; j := 1;
end get_two_nats;
m, n: Natural;
begin -- main
get_two_nats(m, n);
put(m); put(n);
exception
when CONSTRAINT_ERROR =>
put_line("CONSTRAINT ERROR occurred");
end tryexceps;
- Imagine an exception E on the get statement in
NO_handler
- Exception E is passed to
get_two_nats
:
- If E is an END_ERROR, a message is produced, i and j are set to 0,
control is returned to
main
, and the values of i and j are printed
- If E is NOT END_ERROR, then E is passed to
main
:
- If E is CONSTRAINT_ERROR, then a message is printed and the
program halts.
- If E is NOT CONSTRAINT_ERROR, the the program crashes
- Now, imagine an exception E on the get statement in
has_handler
- Exception E is passed to the handler in
has_handler
:
- If E is DATA_ERROR, a message is produced, i and j are set to 1,
control is returned to
get_two_nats
and then to
main
,
and the values of i and j are printed
- If E is NOT DATA_ERROR, then exception E is passed to
the exception handler in
get_two_nats
:
- Execution then continues as in case 1.1 above
Begin/End Blocks
- A begin/end block can be placed around a group of statements
- Begin/end blocks can be used to handle exceptions for specific lines:
with ada.text_io; use ada.text_io;
with ada.integer_text_io; use ada.integer_text_io;
procedure eofloop is
i: Integer;
begin
put_line("This is before the loop");
while not ada.text_io.end_of_file loop
begin
get(i);
put(i);
new_line;
exception -- handles only the 3 lines above
when data_error =>
put_line("You must enter a number ");
when constraint_error =>
put_line("You must enter a natural number");
end;
put_line("This is at the end of the loop");
end loop;
put_line("This is after the loop");
end eofloop;
Exception Flow of Control and Begin/End Blocks
- If a begin/end block contains an exception handler that handles an
exception that occurs:
- Then after the exception is handled, control flows to the
statement following the begin/end block
- If a being/end block contains an exception handler that does NOT handle an
exception that occurs, then the exception is passed to the enclosing procedure's
caller (or to the OS)
- If the caller does not handle the exception, the program crashes
Exception Control Flow
- Consider flow of control in this example:
with ada.text_io; use ada.text_io;
with ada.integer_text_io; use ada.integer_text_io;
procedure exception-example is
procedure sub1(i: out Natural) is
begin
put_line("P: Enter sub1");
begin
get(i);
exception
when data_error => put_line("Q: Data error occurred");
end;
put_line("R: Finished sub1");
end sub1;
n: Natural;
begin
put_line("A: This is before the call to sub1");
begin
sub1(n);
put(n);
exception
when data_error =>
put_line("B: You must enter a number ");
when constraint_error =>
put_line("C: You must enter a natural number");
end;
put_line("D: This is after the exception handler");
end exception-example;
Consider what happens with each of these exceptions:
- constraint_error
- end_error
- data_error
Consider what happens with each of these exceptions:
- constraint_error: A, P, C, D
- end_error: A, P, Crash
- data_error: A, P, Q, R, n printed, D
Declaring and Raising Exceptions
- Exceptions declarations are similar to variable declarations
- Exceptions are raised with the raise statement
- Syntax:
procedure ... is
MyExcep: Exception;
begin
...
raise MyExcep;
raise MyExcep with "Some message";
...
end ...;
Example program: exceptions3.adb
( prettified )
- This example also shows use of functions
ada.exceptions.exception_name(e) and
ada.exceptions.exception_message(e) and
- It also shows declaraing and using an exception parameter
Raise Expressions
- Can be used as an expression
- This is particularly useful in conditional expression
(if n > 0 then 2 ** n else raise Constraint_Error)
Finding and Using an Exception's Name
- This example shows using procedure
ada.exceptions.exception_name(e) to find the name of an exception
and ada.exceptions.exception_message(e) to find the message raised
with the exception
with Ada.Exceptions; use Ada.Exceptions;
procedure exceptions_demo is
num: Integer;
begin
get(num);
put_line("Number is " & num'img);
exception
when e: others => -- e is an exception parameter
put_line(Exception_Name(e));
put_line(Exception_message(e));
put_line("Bailing out!");
end exceptions_demo;
Another Example
- Program purpose: print a table of a range of powers of integers in some range:
- The program uses exceptions to
- allow only integers to be in the input
- control what is printed on overflow (ie if a value is too large)
- The program: Table4.adb ( prettified )