Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

Exceptions and Return Codes

by kschwab (Vicar)
on Jun 29, 2001 at 17:08 UTC ( [id://92594] : perlquestion . print w/replies, xml ) Need Help??

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:

  1. subs that return 0 or undef on failure
  2. subs that die(), expecting the caller to wrap them in an eval block to catch failures, and use $@ to get the error text.
  3. subs that know the errors are OS related and thus set $! or $^E.
  4. subs that put error explanations into globals
  5. subs that call warn(), and expect the caller to muck with $SIG{__WARN__} to get context about the error
  6. use Carp;
  7. 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 ?

Replies are listed 'Best First'.
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

      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...


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@', "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!!!!

      The disadvantage of using a bomb function is that it only performs the intended task if you use bomb. No CPAN module will do so. If you use a __DIE__ handler, you will catch all dies, including those from modules.

      And it has the added benefit that if someone else has to maintain the code, she won't be wondering what this bomb function is for.

      -- Abigail

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

      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.

      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.

        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

      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.


(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.
Re: Exceptions and Return Codes
by MadraghRua (Vicar) on Jun 30, 2001 at 03:19 UTC
    I have to agree with tilly and ichimonki, Carp is vital. Especially for CGI, where I always use Carp(fatalstobrowser).
    Otherwise on subroutines if I'm piping back data, I tend to return an integer > 0 for error messages. Depending on the subroutine, I might have a range of error codes to return if I don't want the program to die there. You might try out page 348 of the Perl Cookbook, which has some suggestions on the undef, wantarray and return functions that you might find useful.
    This link is also good for silly die messages, if you're in the mood.

    yet another biologist hacking perl....

      I sure hope you mean you use "fatalsToBrowser" for development only. Leaving it on in production code is a BIG SECURITY HOLE.

      -- Randal L. Schwartz, Perl hacker