Sather - A Language Manual - Exceptions

[back] [Abstract] [Copyright Notice] [Contents] [next]

# Sather - A Language Manual - Chapter 10
Exceptions

Exceptions are used to escape from method calls under unusual circumstances. For example, a robust numerical application may wish to provide an alternate means of solving a problem under unusual circumstances such as ill conditioning. Exceptions bypass the ordinary way of returning from methods and may be used to skip over multiple callers until a suitable handler is found.

## 10.1 Throwing and Catching Exceptions

There are two aspects to indicating errors using exceptions - how the error is indicated at the point where it occurs. This is usually referred to as throwing the exception. The other aspect of exceptions is how the error message is handled, which is referred to as catching the exception.

### 10.1.1 Throwing Exceptions with raise

Exceptions are explicitly raised by raise statements. The raise statement specifies an expression, which is evaluated to obtain the exception object.

    add_if_positive(i:INT) is
       if i < 0 then
          raise "Negative value:" + i + "\n";
       end;
    end;

In the example above, the object happens to be a string that indicates the problem. In general, the exception object must provide enough information for the error handling mechanism. Since the error handling mechanism can discriminate between different objects of different types, it is standard practice to use the type of the exception object to indicate the type of the error that occurred.

### 10.1.2 Catching Exceptions with protect

Exceptions are passed to higher contexts until a handler is found and the exception is caught. Exceptions are caught using protect statements. The protect statement surrounds a piece of code, and provides an appropriate method of handling any exceptions that might occur when executing that piece of code.

    protect
       foo;
    when $STR then
       #ERR + "An error in foo!:" + exception.str;
    when INT then
       #ERR + "INT error=" + exception; -- 'exception' of type INT
    else
       -- Some other error handling
    end;

When there is an uncaught exception in a protect statement, the system finds the first type specifier listed in the '`when`' lists which is a supertype of the exception object type. The statement list following this specifier is executed and then control passes to the statement following the `protect` statement.

In the protect clause, the exception raised may be referred to by the built in expression `'exception'`[15], which refers to the exception object. The type of the exception object can be used to categorize the exception and to discriminate between exceptions when they are actually caught. In fact, the `when` clauses may be viewed as a `typecase` (see The `typecase` statement, section 5.6) on the exception object.

Points to note

- No statements may follow a `raise` statement in a statement list because they can never be executed.

- If there is no `else` clause in a `protect` statement, and none of the types in the `when` branches matches the type of the exception object, then the exception is passed to the next higher protect statement

### 10.1.3 Usage to avoid

Exceptions can be significantly slower than ordinary routine calls, so they should be avoided except for truly exceptional (unexpected) cases. Using exceptions to implement normal control flow may be tempting, but should be avoided. For instance, in the `STR_CURSOR` class, we can make use of exceptions for parsing. It might be tempting to write code like the following

    test_bool:BOOL is
       protect
          current_state ::= save_state;
          b ::= get_bool;
          restore_state(current_state);
       when STR_CURSOR_EX then
          return(false);
       end;
       return(true);
    end;

The above code determines whether a boolean is present in the string by trying to read one and treating an error state as evidence that there is no boolean. While it is perfectly correct code, this is an example of what you should not do. The implementation of a function should not rely on exceptions for its normal functioning. Doing so is extremely inefficient and can result in an unnecessarily complicated flow of control.

### 10.1.4 Alternatives to Exceptions

The alternative to using exceptions is to use a sticky error flag in the class, as is done by IEEE exceptions and the current `FILE` classes. This has problems such as the fact that the outermost error is logged, not the most immediate one, and it is very easy to forget to test for the error. However, this method has a much lower overhead and is suitable in certain cases.

## 10.2 A more elaborate example

Consider the following routine, which tries to read a boolean value from a string:

    get_bool(file_name:STR):BOOL is
       f:FILE := FILE::open_for_read(file_name);
       if f.error then
          raise #FILE_OPEN_EXC(file_name);
       end;
       s:STR := f.str;  -- Read the file into a string
       f.close;         -- Close the file
       res:BOOL;
       bool ::= "";
       i:INT := 0;
       loop
          until!(~(s[i].is_alpha) or (s[i].is_space) or i >= s.size);
          bool := bool + s[i];
          i := i + 1;
       end;
       case bool
       when "true","t","True","T","TRUE" then
          return true;
       when "false","f","False","F","FALSE" then
          return false;
       else
          raise #PARSE_BAD_BOOL_EXC(s);
       end;
    end;

In the above routine there are two possible errors - either the file could not be opened or it does not contain a valid boolean. The two cases can be distinguised at the point when the exception is caught

    protect
       file_name:STR; ... set to a value
       b:BOOL := get_bool(s);
    when FILE_OPEN_EXC then
       #ERR + "Could not open:" + exception.file_name + "\n";
    when PARSE_BAD_BOOL_EXC then
       #ERR + "Error in reading boolean:" + exception.str + "\n";
    end;

The classes that implement these exceptions can be fairly simple

    class FILE_OPEN_EXC is
       readonly attr str:STR:
       create(file_name:STR):SAME is
          res ::= new;
          res.str := file_name;
          return res;
       end;
    end;

The other exception class is very similar.

[back] [Abstract] [Copyright Notice] [Contents] [next]

Sather - A Language Manual