Exception::Class::TCF - Java/C++ style exception handling
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 };
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.
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 C<new> is a virtual function it can not be called with these shortened package names. For this on can use C<Exception::Class::TCF::make> instead.
An exception is raised by calling the function throw with the
exception as first argument. throw is a prototyped function (See
Prototypes in the perlsub manpage) 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.
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).
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';
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.
The package Exception::Class::TCF exports the following functions
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.
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 };
is just syntactic sugar for Finally = FUN_REF> and hence can be
used as follows
try {} 'Default' => sub {}, finally {...};
or alone
try {} finally {...};
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" };
Apart from these the following functions may also be imported from
Exception::Class::TCF (using the import or use mechanism).
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.
Clears the active exception if there is one and does nothing if not.
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.
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.
If FLAG is true, at a subsequent entry to a try block a signal
handler for __WARN__ (See SIG in the perlvar manpage) 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.
The following are the public virtual functions of Exception::Class::TCF.
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).
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.
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.
for each KEY-VALUE PAIR the VALUE is stored in a field of name KEY. If EXCEPTION is a name nothing is done.
returns the value of the field KEY if the field is set and undef if
it isn't.
removes the fields with names in KEYS from the exception.
return a true value exactly when EXCEPTION has a field named KEY.
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.
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.
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.
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::.
is the root class of all exceptions. Throwing exceptions of this type is not encouraged, use exceptions at the next level.
is the class of errors. Its only special feature is that when thrown
outside of a try block die is called.
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).
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).
is the exception thrown when an assertion has failed. Its package
contains the function assert (which may be imported by other packages).
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 of tricky uses of try may be found in t/Exception.t in
the distribution.
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.
None in the library (that I am aware of).
This module has been written by Torsten Ekedahl (teke@matematik.su.se), and subsequently modified to subclass the Exception::Class manpage and rechristened by Rutger Vos (rvosa@sfu.ca).