kschwab has asked for the wisdom of the Perl Monks concerning the following question:
I wondering if anyone has adopted a consistent approach
to handling ( and generating ) exceptions in perl, including supplying explanatory text about the exception
or failure.
I've seen:
- subs that return 0 or undef on failure
- subs that die(), expecting the caller
to wrap them in an eval block to catch failures, and
use $@ to get the error text.
- subs that know the errors are OS related and
thus set $! or $^E.
- subs that put error explanations into globals
- subs that call warn(), and expect the caller
to muck with $SIG{__WARN__} to get context
about the error
- use Carp;
- etc, etc
I've read perldoc perlvar, and the "Error Indicators" section, as well as the Carp docs, and $SIG{__WARN|DIE__} areas of perlipc.
Does anyone have any thoughts on how I should approach
passing error conditions and return values around in
my own code ?
Re: Exceptions and Return Codes
by merlyn (Sage) on Jun 29, 2001 at 17:45 UTC
|
If a failure is a "normal" thing, a special return value is fine.
If a failure is an "exceptional" thing, I'd prefer a death that I can catch.
If extra information needs to be provided, I'd prefer a package-specific global,
or (using recent Perl exceptions that can handle die $object), a death object
which can be queried.
So there's no one "right answer". It really depends on how frequent and how
complex the failure can get.
And as long as I'm on the soapbox, the wrong answer is "put it into $@",
as some of the IO::* does. That's reserved for an eval failure, Graham.
Usurping that was a bad idea. Bleh.
-- Randal L. Schwartz, Perl hacker | [reply] |
|
AAAAAAAAAAAAArgh, but thank you,
That last one has been bugging me all week, I've been wading through some old IO::Socket and IO::Select code and $@ was being checked a few times with no eval's or do's in sight
And no mention in the perldoc that i could find either, thats one niggle less at least...
cheers
| [reply] |
Re: Exceptions and Return Codes
by Henri Icarus (Beadle) on Jun 29, 2001 at 18:03 UTC
|
This is a great question, and I look forward to others replies to it. The big problem is that there is no one size fits all answer to this question. If your perl is a CGI script you don't want to die before you've printed out some error message. In other cases die is just fine. If you've got a database connection open you may want to clean up so an eval block is crucial. For the purposes of simple CGI's I've used the following general purpose "bomb" routine:
#---------------------------------------------------
# subroutine bomb
# a somewhat ungracefull way to end the execution of the cgi
sub bomb
{
my $error_text = shift;
print "\n<B>Ho Boy! A nasty error has occured:</B> ";
#extra \n to close off the HTTP header
print $error_text;
print "\n";
my @mail;
if ($MAIL_BOMB_LIST ne '') {
&printbomb(\@mail,$error_text);
@BombRecipients = split (/,/, $MAIL_BOMB_LIST);
my $submission = join '',@mail;
$submission = "\n\n".$submission;
$sent = &send_mail ($BombSMTPServer, $submission, 'CGI_Bomber@
+acme.com', "CGI $ENV{'SCRIPT_NAME'} bombed.", $BombReplyto, @BombReci
+pients);
}
exit;
}
# load error message and backtrace into @$array
sub printbomb
{
my $array = shift;
my $message = shift;
my($syserror) = $!;
push @$array, <<"EOM";
The CGI script $ENV{'SCRIPT_NAME'} seems to be having trouble!
Error Message: $message
Sys Error is: $syserror
EOM
my $i = 2; # start back 2 so as not to print bomb & printbomb s
+tack frames.
push @$array, "Backtrace:\n";
push @$array, " Subroutine name | Line |
+ File \n";
push @$array, "----------------------|------|---------------------
+----------------------------\n";
while(($pack,$file,$line,$subname) = caller($i++)) {
push @$array, sprintf("%21s |%5s |%50s\n",$subname,$line,$file
+);
}
}
This code is obviously not the neatest, but it's very nice for debugging. It simply prints out the error message, but then e-mails the stack backtrace to the emails in the global $MAIL_BOMB_LIST
-I went outside... and then I came back in!!!! | [reply] [d/l] |
|
| [reply] [d/l] [select] |
Re: Exceptions and Return Codes
by Abigail (Deacon) on Jun 29, 2001 at 18:37 UTC
|
Does anyone have any thoughts on how I should approach passing error conditions and return values around
in my own code ?
That question doesn't belong in the spirit of Perl. If you
want others to decide how you should approach things, you
should be using Python, or Java. If you program Perl, you should use what you
think is right for you.
I can however, tell you what I usually use. I tend to
die for unexpected failures - for instance,
failure to open a file, or wrong types of arguments.
(But note that Dennis Ritchie once said that a failure to
open a file was hardly exceptional). For failures that could
be expected (for instance, a search in a datastructure that
doesn't find anything), I typically return undef
or an empty list, but sometimes 0, or if I
want to give some information why a failure occured, I
return some predefined constant - negative numbers, or
strings, depending what is more appropriate. It should, after
all, be possible to determine which return values signal
failures, and which ones are normal return values. Sometimes
I return a 2-element list, one element indicates success/failure,
the other the return values or failure reason.
I tend not to use globals for explanations, except $@
and to a lesser extend $!. I never, never use
warn to do internal message passing. warn
IMO, communicates to the user, not the rest of the program.
I may use warn in combination with the other
failure flagging techniques.
Carp is just a wrapper around die
and warn, and I do not consider that a different
technique.
-- Abigail | [reply] [d/l] [select] |
|
If that question does not belong in the spirit of Perl, then neither does Very very small style question from Dominus.
Likewise the core documentation should cut out perlstyle, it is obviously encouraging the wrong attitudes towards Perl.
Now if I want someone else to tell me how to program, then I will use Python or Java. But if I stop looking for suggestions on how I might be able to program better, then I will look for another career.
Aside from that, I tend to do something similar to the rest of your answer except with the caveat that I think of Carp as being different from die and warn in that it makes it easy to get useful context. Also in CGI programming warn becomes a way to communicate with the server logs rather than the user. This distinction is often very useful.
| [reply] |
|
Abigail you are more knowledgeable and active in the Perl community than I am.
But on this you are wrong. If I do not know what is the best way to handle failed subroutine calls, then it is reasonable for me to find out what others have determined is best for them.
Because what is best for others who have approached the same problem is probably, 97 percent of the time, best for me. So I say that one should, when learning Perl, let the experience of others direct you when it is not clear what direction you should take on your own.
What should you as a Perl programmer do when you are "on your own, with no direction home, a complete unknown, like a rolling stone?" Plagiarize the code of others, I say.
| [reply] |
|
You didn't ask what's best for them. You asked what you
should use, without giving any information what kind of
failures you wanted to signal.
Plagiarize the code of others, I say
That's about the worst you can do. It's the fast way to make
a very bad coder out of you. It's important to understand
what you are doing. Just asking what you should do, and
blindly copying that if you would have gotten an unambigious
answer is very, very bad.
-- Abigail
| [reply] |
|
That question doesn't belong in the spirit of Perl. If you want others to decide how you should approach things, you should be using Python, or Java. If you program Perl, you should use what you think is right for you.
Aside from the above, lots of useful info, thanks.
I do have to reply to the above though. What's that about?
Looks like I touched a nerve. When I say "Does anyone have any
thoughts...", I'm looking for suggestions and ideas
from others. I'll use those suggestions along with my
own thoughts to come to a conclusion. Since other folks
might end up using code I've written, I was hoping to
get a perspective on what they might expect. Yeesh.
| [reply] |
(ichimunki) Re: Exceptions and Return Codes
by ichimunki (Priest) on Jun 30, 2001 at 02:28 UTC
|
I like use Carp; combined with a sensible combination of the Carp methods, using non-fatal warnings for some things, and using fatal warnings for most things (especially during development since you WANT the program to die so you have to fix the error).
Using Carp allows you to override the error handlers later without too much difficulty and substitute your own user-friendly error messages for production.
Also, Carp does some really nice things with stack tracing when you are using OO Perl. | [reply] [d/l] |
Re: Exceptions and Return Codes
by MadraghRua (Vicar) on Jun 30, 2001 at 03:19 UTC
|
| [reply] |
|
| [reply] |
|
|