NAME

Exception::Class::TCF - Java/C++ style exception handling

SYNOPSIS

try BLOCK [ [catch] NAME FUN_REF ]*

throw [ EXCEPTION LIST ]

package EnclosureException;
@ISA = qw(Exception::Class::TCF);

package main;

use Exception::Class::TCF;

try {
    if ($Lost) {
        throw new EnclosureException Message => "Help!";
    }
    else {
        throw Error;
    }
}
catch 'EnclosureException' => sub { 
    warn "Message ",$_[0]->message," received.\n" 
},
'Default' => sub { 
    warn $_[0]->type, " exception ignored, trace:", $_[0]->trace 
};

DESCRIPTION

The Exception::Class::TCF module provides the possibility of executing a code block and specifying actions when different exceptions are raised. The try function takes as its argument a code block followed by a list of pairs of exception package names and function references, representing the action to take if a subclass of that package is raised. To increase readability the keyword catch may be inserted before any name-action pair. The return value of try is the return value of the block if no exception is thrown and the return value of the action of the chosen action in case one is found.

Even though the builtin die is used in the implementation any explicit use of die within the dynamic scope is ignored by the exception mechanism and thus works as usual. On the other hand an eval block will catch a thrown exception if it has not been caught by a try block. The clean-up routines after such a block may call throw as in the next section.

How to create an exception context.

An exception context in which thrown exceptions are handled is created using try as in

try { throw 'Error' } 
  catch 'Default' => sub { warn "Wow" };

The first argument is a code block (or a function reference). It will be referred to as a try block and any code executed inside it (including psossibly nested calls of functions in it) will be said to be in the dynamic scope of the block. After the try block follows a sequence of exception name - handling code pairs. The name will be referred to as the exception key and the corresponding code the handler (or catch handler) for that key. An exception is either the name of a package inheriting from the package Exception::Class::TCF or an object blessed in such a package. In both cases the name of the package will be referred to as the name of the exception. All exception keys has to be names of exceptions except the special exception key Default which is the name for exceptions of package Exception::Class::TCF. In order not to clutter package name space, package names are normally prefixed by the Exception::Class::TCF:: prefix. To increase readability this prefix may be removed in exception key names and when calling throw with a package name as first argument.

The exception key may also be the string Finally. This does not correspond to an exception but instead its handler will be called just before the try function returns. Its value will be ignored however.

As new is a virtual function it can not be called with these shortened package names. For this on can use Exception::Class::TCF::make instead.

How to raise an exception.

An exception is raised by calling the function throw with the exception as first argument. throw is a prototyped function (See "Prototypes" in perlsub) so that one may dispense with parentheses.

throw Exception::Class::TCF;

throw 'MyException', "Serious problems";
  # is the same as
throw('MyException', "Serious problems");

throw new Exception::Class::TCF::Error Message => "Hello up there!";
  # is the same as
throw make 'Error', Message => "Hello up there!";
  # and as
Exception::Class::TCF::Error->new(Message => "Hello up there!")->throw;

(The last as Exception::Class::TCF::Error inherits from Exception::Class::TCF which is where throw lives.)

throw without any arguments can also be used to rethrow the active exception. If no exception is active throw raises a die with the argument "Rethrow without an active exception at FILE line LINE\n" where FILE and LINE refer to the place where the exception was thrown. To test if there is an active exception one may use Exception::Class::TCF::isThrowing.

The rules for determining the active exception are the following.

  • Before entering a try block the active exception (if there is one) will be put away and no exception will be active. When the try block is exited the original active exception is restored or there will be no active exception if none existed.

  • Whenever an exception is thrown it becomes the active exception.

  • The active exception may be cleared using Exception::Class::TCF::deactivate which will clear the active exception (and do nothing if there is none). This is primarily useful when an eval block has caught an exception (see next paragraph).

  • Thus normally there will only be an active exception in a handler (and it will be the exception thrown) and then only when one stays at the same "try level"; if one enters a try block inside a handler the active exception will be temporarily cleared (not clearing it would seem to lead to mental confusion as to which collection of handlers will handle the rethrown exception). There is however one other situation that may create active exceptions. As throw uses die internally, any eval block will catch a thrown exception and that exception will remain active as the enclosing try block has not been left (if there is no enclosing try block the throw will already have been turned into an ordinary die). The clean-up routines for such an eval block can use Exception::Class::TCF::isThrowing to check if the die was due to a throw and could then decide to throw the exception or maybe clear it using Exception::Class::TCF::deactivate.

How long an exception lives

The throw mechanism keeps a reference to a thrown exception as long as it can still be rethrown. Hence a DESTROY method for the exception will not be called until the exception may no longer be thrown (and possibly even later if there are some references to it outside the mechanism).

How a handler is chosen.

The exception that is raised in the dynamic scope of a try block is supposed to be a reference blessed in a package inheriting from the package Exception::Class::TCF or the name of such a package. When raised, by calling throw on it, each exception key is considered and it is checked whether or not the thrown exception inherits from the package corresponding to the exception key. The first such exception key is then picked out and its catch handler is called. If none is found the exception is rethrown to be caught by another try block enclosing the given. (This description is not quite true for the exception name Die. See "PREDEFINED EXCEPTIONS".)

If no enclosing block exists, the virtual function die is called on the exception. The default behaviour of die is to call the builtin die with string argument the string obtained by calling sprintf with the name of the exception (i.e. either its own name if it is a package name or the name of its package) as second argument. The first argument is the default string "Exception of type %s thrown but not caught" unless the exception is an object and its DyingMessage has been set in which case the value of that field is used.

Thus the following code

try { throw 'Error' };

will result in die being called with the argument "Exception of type Error thrown but not caught". (Actually when the string does not end with a newline a string of the type "at FILE line LINE\n" is added where FILE and LINE refers to where throw was called. The following will have the same effect:

throw 'Error';

How a handler is called.

A chosen action will be called with the same argument list as the throw. Thus the exception will be the first argument. For example

try {
   throw 'Error', "basement";
} catch 'Default' =>  sub { warn "Mouse found in $_[1]\n" };

will print

Mouse found in basement

on STDERR.

CLASS INTERFACE

Exported functions

The package Exception::Class::TCF exports the following functions

try BLOCK [ [catch] EXCEPTION_NAME => FUN_REF ]*

sets up an environment where a thrown exception in the dynamic scope of BLOCK (and not caught by some inner try block) is matched against the EXCEPTION_NAME's and if matched the corresponding FUN_REF is called. If no matching is found the exception is rethrown.

catch EXCEPTION_NAME => FUN_REF, LIST

gives syntactic sugar for the handler part of a try. That means that the following three expressions are equivalent.

try {} 'Default' => sub {}, 
             NewException => sub { die };

try {} catch 'Default' => sub {}, 
             NewException => sub { die };

try {} catch 'Default' => sub {}, 
       catch 'NewException' => sub { die };
finally FUN_REF

is just syntactic sugar for Finally = FUN_REF> and hence can be used as follows

try {} 'Default' => sub {}, finally {...};

or alone

try {} finally {...};
throw EXCEPTION, ARGS

throws EXCEPTION - the ARGS are passed to the action that catches the exception.

If used without arguments it can be used to rethrow an exception in either of the following situations:

  • Throw an exception out of a handler which is handling it.

  • Throw an exception in the same try block that it was originally thrown. This is possible if it was originally caught by an eval block. An example may look like this.

    try {
       eval {
           &mayDie if $daring; # may exit by a die
           throw 'Exception::Class::TCF' if $exit;
       }
       throw if &Exception::Class::TCF::isThrowing;
       warn "Died on me!\n" if $@;
    } catch 'Default' => 
             sub { warn "Something exited\n" };

Public functions

Apart from these the following functions may also be imported from Exception::Class::TCF (using the import or use mechanism).

make EXCEPTION_NAME, LIST

an interface to new which allows EXCEPTION_NAME to be without the prefix Exception::Class::TCF::. make checks to see if EXCEPTION_NAME is the name of an exception type, if not it checks if Exception::Class::TCF::EXCEPTION_NAME is such a name and if it is Exception::Class::TCF:: is prepended to EXCEPTION_NAME. If it in this way finds an exception type it calls

new EXCEPTION_NAME LIST

if not it returns undef.

deactivate

Clears the active exception if there is one and does nothing if not.

isThrowing

Returns a true value exactly if one is still inside a dynamic try block in which the latest exception was thrown or a handler for that block. This means that one is allowed to call throw without arguments to rethrow the exception.

handleDie FLAG

If FLAG is true subsequent invocations of die in a try block will throw an exception of name Die (See "PREDEFINED EXCEPTIONS") with the string that die constructs as first argument. If FLAG is turned off this behaviour will be turned off. The default behaviour is that an exception key named 'Die' will catch a die but no searching for exception keys above Die in the inheritance will be made.

handleWarn FLAG

If FLAG is true, at a subsequent entry to a try block a signal handler for __WARN__ (See "SIG" in perlvar) will be installed. When warn is called it will throw an exception of type Warning (See "PREDEFINED EXCEPTIONS") unless handleWarn has been called with a false argument in the mean time, in which case it will call the usual warn. When leaving a try block (or one of its handlers) this signal handler will be deinstalled and any old value restored. If FLAG is false this feature will be turned off.

As this requires fiddling with the __WARN__ handler it could be somewhat dangerous and lead to unexpected results. Thus handleWarn may be removed in future versions if disadvantages will turn out to outweigh advantages.

Public virtual functions

The following are the public virtual functions of Exception::Class::TCF.

new EXCEPTION_NAME [ VALUE ] [KEY => VALUE]*

creates an exception in the package EXCEPTION_NAME and for each KEY-VALUE PAIR the VALUE is stored in a field of name KEY. The fields may also be set using setFields so that

$exc = new Exception::Class::TCF::Error 'Timeout' => 5;

is equivalent to

$exc = new Exception::Class::TCF::Error; 
setFields $exc 'Timeout' => 5;

(Unless Timeout should happen to be a protected field in which case the second version will not set any fields.)

In the case of the field named Message the key may be dispensed with provided that it comes first (in other words if the list of arguments - minus the exception name - has odd order, Finally is prepended to it).

die EXCEPTION ARGS

called when EXCEPTION is thrown outside of a try block. This includes when it is thrown in a handler of a try block not contained in another block.

type EXCEPTION

returns the type of the exception, which is the exception itself if it is a package name and the name of its package if it is not. If the package name is prefixed with Exception::Class::TCF:: that prefix is removed.

setFields EXCEPTION [KEY => VALUE]*

for each KEY-VALUE PAIR the VALUE is stored in a field of name KEY. If EXCEPTION is a name nothing is done.

getField EXCEPTION KEY

returns the value of the field KEY if the field is set and undef if it isn't.

removeFields EXCEPTION KEYS

removes the fields with names in KEYS from the exception.

hasField EXCEPTION KEY

return a true value exactly when EXCEPTION has a field named KEY.

protectedFields EXCEPTION

some fields may be protected which means that they can not be modified. protectedFields returns a list of the names of the fields that can not be modified using removeFields or setFields.

setMessage EXCEPTION VALUE

sets the message field of EXCEPTION to VALUE.

message EXCEPTION

is equivalent to

getField EXCEPTION Message.

All these virtual functions except new accepts either the name of a package inheriting from Exception::Class::TCF or a reference blessed in such a package (new only accepts a package name). The former case should be kept in mind when overriding any of these functions in a subclass. In the latter case the reference is assumed to have been created with new.

Implementation details.

These details are not likely to change but should not be considered part of the public interface.

The exception objects are implemented as references to hashes. The field Message is reserved for internal use by message and setMessage. The field DyingMessage is used for the message given when the exception is thrown outside a try block. Arguments to new are stored in the hash.

PREDEFINED EXCEPTIONS

While any number of exception types may be created by making classes inheriting from Exception::Class::TCF some are predefined to give standard names to standard exceptions. All of these packages are in the package Exception::Class::TCF and their names all start with Exception::Class::TCF::.

Exception::Class::TCF

is the root class of all exceptions. Throwing exceptions of this type is not encouraged, use exceptions at the next level.

Exception::Class::TCF::Error

is the class of errors. Its only special feature is that when thrown outside of a try block die is called.

Exception::Class::TCF::Warning

is the class of less serious errors. Its only special feature is that when thrown outside of a try block warn rather than die is called. It is also the exception type which is thrown by warn when the interpretation of calls by warn as throwing an exception has been enabled (See "handleWarn").

Exception::Class::TCF::Die

is the exception that conceptually is raised when die is called inside a try block or catch handler. It has the special feature that normally a Die exception is not caught by exception keys higher up in hierarchy. This behaviour can be changed (See "handleDie").

Exception::Class::TCF::AssertFailure

is the exception thrown when an assertion has failed. Its package contains the function assert (which may be imported by other packages).

assert BLOCK LIST

BLOCK is evaluated and if it returns a false value, an exception of type AssertFailure is created using new with LIST as argument and then thrown. For instance

use Exception::Class::TCF;
use Exception::Class::TCF::AssertFailure qw(&assert);

sub fac {
  my($n) = shift;
  assert { $n >= 0 && int($n) == $n }
       'Message' => "$n is not a positive integer.\n";
  $n == 0 ? 1 : &fac($n -1)*$n;
  }

try { fac(-3) } 
  catch 'AssertFailure' => 
      sub { warn $_[0]->message };

EXAMPLES

Examples of tricky uses of try may be found in t/Exception.t in the distribution.

FEATURES RISKING EXTINCTION

The use of "handleWarn" risks messing up __WARN__ signals and may therefore be removed, it depends on how much trouble it causes vs. how useful it turns out to be.

BUGS

None in the library (that I am aware of).

AUTHOR

This module has been written by Torsten Ekedahl (teke@matematik.su.se), and subsequently modified to subclass Exception::Class and rechristened by Rutger Vos (rvosa@sfu.ca).