Exception handling in Perl
An exception is an event that occurs during the execution of a program that prevents it from the normal execution. Different types of errors can cause exceptions. They can range from serious errors such as running out of virtual memory to simple programming errors such as trying to read from an empty stack or opening an invalid file for reading. An exception usually has what type of exception it is, where the exception occurred, and context information. An exception handler is a piece of code used to gracefully deal with the exception.
Advantages of exception handling
Object-oriented exception handling allows you to separate error-handling code from the normal code. As a result, the code is less complex, more readable and more efficient. The code is more efficient because the normal execution path doesn't have to check for errors. As a result, valuable CPU cycles are saved. Another important advantage of OO exception handling is the ability to propagate errors up the call stack. This happens automatically without you, the programmer, explicitly checking for return values and returning them to the caller. Passing return values up the call stack is error prone, and with every hop there is a tendency to lose vital bits of information.
Most of the time, when an error occurs, it is rarely the best place to handle it. The error needs to be propagated up the call stack b by the time the error reaches the place where it can be handled properly, a lot of the error context is lost. This is a common problem with traditional error-handling. Exceptions come to the rescue by allowing contextual information to be captured at the point where the error occurs and propagate it to a point where it can be effectively used/handled.
Built in exception handling
Perl has a built in exception handling mechanism known as an eval {} block. It is implemented by wrapping the code that needs to be executed around an eval block and the $@ variable is checked to see if an exception occurred. Within the eval block, if there is a syntax error or runtime error, a die statement is executed, then an undefined value is returned by eval, and $@ is set to the error message. If there was no error, then $@ is guaranteed to be a null string.
Problems with eval block
The problem with this is that since the error message store in $@ is a scalar, checking the type of error that has occurred is error prone. $@ doesn't tell us where the exception occurred. Eval blocks can be used for both, building dynamic code snippets as well as for exception handling. Stack trace if required, needs to maintained by writing custom code.
Error.pm solves the issues
The Error.pm module implements object Oriented exception handling. It mimics the try/catch/throw syntax available in other object oriented languages like Java and C++ . It doesn't have all the problems that are in the eval block. Since it's a pure perl module, it runs on almost all platforms where Perl runs. An exception handler is constructed by enclosing the statements that are likely to throw an exception within a try block. If an exception occurs within a try block, it is handled by the catch block, associated with the try block. If no exceptions are thrown, then try will return the result of block. The try block associates the scope of its associated exception handlers. You associate exception handlers with a try block by providing one or more catch blocks directly after the try block. The order of exception handlers is important. It’s all the more critical if you have handlers at different levels in the inheritance hierarchy. Exception handlers that are built to handle exception types that are furthermost from the root of the Error hierarchy should be placed first in the list of catch blocks. An exception handler designed to handle a specific type of object may be pre-empted by another handler whose exception type is a superclass of that type. This happens if the exception handler for that exception type appears earlier in the list of exception handlers.
try { my $result = $self->divide($value, 0); return $result; } catch MathException with { my $ex = shift; print "Error: Caught MathException occurred "; return; } catch DivideByZeroException with { my $ex = shift; print "Error: Caught DivideByZeroException "; return 0; };
Finally block
The final step in setting up an exception handler is providing a way for cleaning up before control is passed to different part of the program. This can be done by enclosing the cleanup logic within the finally block. Code in the finally block is executed no matter what happens within the try block. Typical use of the finally block is to close files or in general to release any system resource. If no exceptions are thrown, then none of the code in the catch blocks gets executed. But the code in the finally block is always executed. If an exception is thrown, then code in the appropriate catch block is executed. Once the execution of that code is complete, the finally block is executed.
try { my $file = join('.', '/home/foo.txt', $$); my $filehndle = new FileHandle($file, 'w'); throw IOException("Unable to open file - $!") if (!$filehndle); return; } catch Error with { my $errorcode = shift; } finally { close($filehndle) if ($filehndle); unlink($file); };