Friday, March 30, 2007

A brief introduction to benefits of exceptions

Every programmer of a programming language has to cope with errors/exceptions and error/exception handling on a "it's all in a job" - base nowadays. But astoundingly often, exception handling and it's (more or less) proper or inteded usage is highly discussed all over and over again. This brief introduction wants to show that exception handling is a powerful tool and may be a shortcut for normal error handling. By using exception handling, programs even get more robust and less error-prone.

A tiny example without
exception handling.

A program works using the IPO-principle: Input-Processing-Output. If the input is well defined and the processing is correct, the output is correct too. The following example is a little program that reads three numbers (line by line) from a text file with a given name, adds them up and prints the sum as a result, "written" step by step:
  1. Read the name of the file as parameter of the program into variable "fileName"
  2. Assing a variable "sum" with the value "0" (zero)
  3. Open the file "fileName"
  4. Repeat steps 5 and 6 for 3 times
  5. - Read one line into the variable "line" as text
  6. - Convert the text "line" into a number and add it to "sum"
  7. Close the file "fileName" (the resource handler needs to be freed)
  8. Print "sum" on the screen
The processing of this simple excample is correct, but there are pitfalls that might stop our program from working correctly depending on the input:
  • The program did not get a parameter to store it in "fileName"
  • The file "fileName" does not exist or cannot be opened
  • There is nothing in the file, or, more precisely, there are not at least three lines in the file
  • One of the three lines is/are not numerical and cannot be converted into a number

Programming this example without exception handling means that you have to check each of these error sources step by step (check-list):
  • if no parameter is given, go to "exit" with error "No parameter given" (1.)
  • if the file "fileName" does not exist, go to "exit" with error "File does not exist" (3.)
  • if the file cannot be opened, go to "exit" with error "File cannot be opened" (3.)
  • if the next line cannot be read, go to "exit" with error "Cannot read number" (5.)
  • if the line is not a number, go to "exit" with error "Cannot read number" (6.)
  • write the sum
  • "exit": if the file is open, close it.
This example is written in a defensive kind and shows that the most part of programming often is not the program-logic itself. Moreover it is error handling and reporting, because the user wants to know why he did not get the sum.

Using exception handling.

Using a language that utilizes exception handling will help a lot in this example. At first, the methods provided by the language will provide you with (more or less) reasonable built-in exception handling, i.e. the method "open file" will raise an (input/output) exception if the file cannot be opened or does not exist or even if the parameter for the file name is invalid. This way you will not have to check for the file of your own (though you could) but rather catch an appropriate exception and print an according error message. You define the range where an exception will be "inspected" through the try-catch - block (pseudo-code):

try {
openFile()
do(3-times) {
try {
readNumberAndAddToSum // Assuming that an exception is thrown if no number is applicable
}
catch(NotANumberException) {
closeFile() // because it already is open
printNaNError
exit
}
}
closeFile()
printSum
}
catch(IOException) {
printIOError
}
Wherever an input/output exception occurs (here in the method "openFile()"), the code will stop at this line and jump into the "catch(IOException) { ... }" - block. This is especially true in case the "readNumberAndAddToSum" will throw an IOException (i.e. end of file). The handling of a type of exceptions can be centralized for the whole algorithm this way.

Additionally, a NotANumberException is catched in this example too, the program then terminates. If the IO - routines and the number converting routines work correctly (i.e. throw the right exceptions), this little snippet fulfills all the "if / exit" - assumptions done earlier, without writing more lines of code.

That's all folks?

As shown above, exception handling can shorten and, in a way, clarify the source code. A detailed "which-errors-happen-if" matrix does not need to be as detailed as a matrix that shows all possibilities when only using "if ... then ... else if ..." - constructs (the check-list above). Simply try to write the program with complete error handling using the check-list and you will see the difference.

Let us enhance the original purpose: The little example of reading three numbers matured and needs to be integrated in a larger program. This way the program cannot simply be stopped via "exit" if an error occurs, we have to re-write it as a method returning the sum as a number to fit into the whole program. The question is, case an error occurs, which value should be returned? Normally, errors are indicated using the "-1" - value. But the sum could be "-1" which then will be interpreted as an error.

Throwing an exception or letting an exception through (by not catching it explicitely) is the answer to this problem. By changing the intention of the algorithm ("simply printing the sum" to "returning the sum as a subpart of a larger program"), the scope of the method changed from a single program to a part of a program. So the algorithm cannot come to a conclusion on how to react on errors any more, because it does not know the exact state it was called from (compute - print - terminate is not an option any more). Anyway, the calling instance of the algorithm can do this because it knows why it called the method, it may print an error message or show an error dialog, it may ignore the error or create a file for later processing etc. . So the caller of our newly created method needs to know what exactly went wrong and will get this information from the exception thrown: "Couldn't I read the file? IOException". "The input went wrong because there was no number?
NotANumberException".

So, exception handling is ...

... an additional possibility to handle exceptional behaviour and errors in an efficient, readable and maintainable way, if used correctly. It provides you from getting headaches by declaring detailed error matrixes or by providing the possibility to even catch errors you didn't think of in first place. And remember: A segmentation fault only is an exception never catched.

Labels: ,

 

2 Comments:

Anonymous Anonymous said...

Nice writeup.

How about a writeup on higher-level exception handling? When to catch? When to throw? Who is responsible for what exceptions? In a larger program, these are inevitably the questions that appear.

7:09 PM  
Blogger Georgi said...

Your wish is my dethire, marther: http://goit-postal.blogspot.com/2007/05/thoughts-on-successful-exception.html

Greetings, Igori :)

1:16 PM  

Post a Comment

<< Home