Die with style when something goes wrong.
Perl's exception handling is sufficiently minimal. It's easy to recover when things go wrong without having to declare every possible type of error you might possibly encounter. Yet there are times when you know you can handle certain types of exceptions, if not others. Fortunately, Perl's special exception variable $@
is more special than you might knowâit can hold objects.
If you can stick an object in there, you can do just about anything.
How would you like more context when you catch an exception? Sure, if someone uses the Carp
module you can sometimes get a stack trace. That's not enough if you want to know exactly what went wrong.
For example, consider the canonical example of a system call gone awry. Try to open a file you can't touch. Good style says you should die(â)
with an exception there. Robust code should catch that exceptionâbut there's so much useful information that the exception string could hold, why should you have to parse the message to figure out which file it was, for example, or what the error was, or how the user tried to open the file?
Exception::Class
lets you
throw an exception as normal while making all of the information available through instance methods.
Suppose you've factored out all of your file opening code into a single function:
use File::Exception; sub open_file { my ($name, $mode) = @_; open my $fh, $mode, $name or File::Exception->throw( file => $name, mode => $mode, error => $! ); return $fh; }
Instead of calling die(â)
, the function throw(â)
s a new File::Exception
object, passing the file name, mode, and system error message. File::Exception
subclasses Exception::Class::Base
to add two more fields and a friendlier error message:
package File::Exception; use SUPER; use Exception::Class; use base 'Exception::Class::Base'; sub Fields { my $self = shift; return super(â), qw( file mode ); } sub file { $_[0]->{file} } sub mode { $_[0]->{mode} } sub full_message { my $self = shift; my $msg = $self->message(â); my $file = $self->file(â); my $mode = $self->mode(â); return "Exception '$msg' when opening file '$file' with mode '$mode'"; } 1;
The only curious piece of the code is the Fields(â)
method. Exception::Class::Base
uses this to initialize the object with the proper attributes.
full_message(â)
creates and returns the string used as the exception message. This is what $@
would contain if this were a normal exception. As it is, Exception::Class::Base overrides object stringification [Hack #99] so the objects appear as normal die(â)
messages to users who don't realize they're objects.
Call open_file(â)
as usualâwithin an eval(â)
block:
my $fh; $fh = eval { open_file( '/dev/null', '<' ) }; warn $@ if $@; $fh = eval { open_file( '/dev', '>' ) }; warn $@ if $@;
Reading from /dev/null is okay (at least on Unix-like systems), but writing to /dev or any other directory is a problem:
Exception 'Is a directory' when opening file '/dev' with mode '>' at directory_whacker.pl line 10.
The real power comes when you treat the object as an object:
$fh = eval { open_file( '/dev', '>' ) }; if (my $error = $@) { warn sprintf "Tried to open %s '%s' as user %s at %s: %s\\n", $error->mode(â), $error->file(â), $error->uid(â), scalar( localtime( $error->time(â) ) ), $error->error(â); }
What are the other methods? They're methods available on all Exception::Class
objects.
Tip
Make a copy of $@
as soon as possible, lest another eval(â)
block somewhere overwrite your object out from underneath you.
Now instead of having to parse the string for potentially useful information, you can debug and, if possible, recover with better debugging information:
Tried to open > '/dev' as user 1000 at Tue Jan 17 21:58:00 2006: Is a directory
Exception::Class
objects are objectsâso they can have relationships with each other. You can subclass them and make an entire hierarchy of exceptions, if your application needs them. You can also catch and redispatch them based on their type or any other characteristic you want.
Best of all, if someone doesn't want to care that you're throwing objects, she doesn't have to. They still behave just like normal exceptions.
Get Perl Hacks now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.