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 thetry
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 aneval
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. Asthrow
usesdie
internally, anyeval
block will catch a thrown exception and that exception will remain active as the enclosingtry
block has not been left (if there is no enclosingtry
block thethrow
will already have been turned into an ordinarydie
). The clean-up routines for such aneval
block can useException::Class::TCF::isThrowing
to check if thedie
was due to athrow
and could then decide tothrow
the exception or maybe clear it usingException::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 followstry {} '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 aneval
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 prefixException::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 callsnew 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 callthrow
without arguments to rethrow the exception. - handleDie FLAG
-
If FLAG is true subsequent invocations of
die
in atry
block will throw an exception of nameDie
(See "PREDEFINED EXCEPTIONS") with the string thatdie
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 adie
but no searching for exception keys aboveDie
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. Whenwarn
is called it will throw an exception of typeWarning
(See "PREDEFINED EXCEPTIONS") unlesshandleWarn
has been called with a false argument in the mean time, in which case it will call the usual warn. When leaving atry
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. ThushandleWarn
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 atry
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 usingremoveFields
orsetFields
. - 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
blockdie
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
blockwarn
rather thandie
is called. It is also the exception type which is thrown bywarn
when the interpretation of calls bywarn
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 atry
block or catch handler. It has the special feature that normally aDie
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 usingnew
with LIST as argument and then thrown. For instanceuse 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).