Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Returning Error Codes

by qazwart (Scribe)
on May 25, 2006 at 20:19 UTC ( #551690=perlquestion: print w/replies, xml ) Need Help??
qazwart has asked for the wisdom of the Perl Monks concerning the following question:

I am writing a Perl module, and I'd like to return an error code of sorts if there is a problem with the fuction. Normally, you simply return a zero on an error and a non-zero on a normal return. That way you can do this:
if (foo()) { print "Everything is copacetic!\n"; } else { print "Whoops! Something went terribly wrong!\n"; }
So, if foo() returns a zero, I know there's an error. However, I don't know what type of error I got. What I'd like to do is something like this:
if (foo()) { print "Everything is copacetic!\n"; } else { print "Error: $!\n"; }
Now, I know this won't work because $! is for system errors, and I'm returning my own personal error. However, it would be nice to be able to test the return value of my function, and if it fails, give you a reason why I'm returning an error. So, is there anyway of doing this? (Yes, I know I could return an array, so the first value is my return value and the second is my error message, but then the "if" statement doesn't test the function directly:
($value, $errorMsg) = foo(); if ($value) { print "Everything is copacetic\n"; } else { print "Error: $errorMsg\n"; }

Replies are listed 'Best First'.
Re: Returning Error Codes
by gellyfish (Monsignor) on May 25, 2006 at 20:31 UTC

    A lot of modules will return a false value and set a package variable that contains the error for instance $DBI::errstr or alternatively a method that can be called to retrieve the actual error indicated by the false return value.

    Alternatively "throw an exception" with die and "catch" the error with an eval block and retrieve the error message in $@


Re: Returning Error Codes
by philcrow (Priest) on May 25, 2006 at 20:34 UTC
    Perl Best Practices recommends dying when you have an error. This prevents oversight by the caller (where they forget to test for the return code and go merrily on their way with flawed data in hand). This avoids downstream bug appearance which is harder to track. Then, you can say something like this:
    eval { foo(); }; if ( $@ ) { # handle the error the error is in $@ }
Re: Returning Error Codes
by ioannis (Prior) on May 26, 2006 at 02:07 UTC
    The Perly solution is to exploit the hash capability of the *@ glob -- it is a well-kept secret. You don't want the function to set a scalar or set an array. You want the function to set the 0 key of a hash -- it is easier to test the return value that way.

    (Those who answered with die(), with 'best practices', or even with 'common practices' are avoiding to answer directly the poster's question.)

    foo(); $@{0} ? print "error: $@{0}" : print 'ok'; sub foo { $@{0} = 'proxy error' }
Re: Returning Error Codes
by Sandy (Curate) on May 25, 2006 at 20:48 UTC
    May not be the best practise in the world, but this works like you asked.

    'foo' returns array, and 'if' statement looks like

    if ((($r,$m) = foo($i))[0]) {
    #!/usr/bin/perl use strict; my ($m, $r); foreach my $i (1 .. 2) { print "\n"; if ((($r,$m) = foo($i))[0]) { print "It worked\n"; } else { print "error: $m\n"; } } sub foo { my $num = shift; my @ret = (); if ($num == 1){ print "inside foo, good\n"; @ret = ($num,"good input number"); } else { @ret = (0,"bad input number"); } return @ret; }
Re: Returning Error Codes
by duckyd (Hermit) on May 26, 2006 at 04:48 UTC
    Another poor practice - you could adopt the unix exit code strategy. Return 0 or undef for success, and an error code in other cases. Then you can do
    unless( my $error = foo() ){ # do some stuff }else{ warn "whoops, foo() failed, error code: $error\n"; }
    Really, I'd suggest the die strategy, but this is closer to what you desired....
Re: Returning Error Codes
by qazwart (Scribe) on May 30, 2006 at 15:05 UTC
    The sentiments on this reply say "die" which is normally what I'd like to do, but in this particular case, a failure isn't necessarily fatal.

    I'm creating a Perl interface to a third party program, and in this case, I am reading an attribute. If the attribute exists, I return its value, if not, I return nothing. However, I might be returning nothing because the value isn't set, or I might be returning nothing if the user's login credentials expired, or I might be returning nothing because the server is down.

    Dying because the return value is unavailible isn't set is not good. Maybe the user wants to set the attribute if it isn't already set. At the same time, if the server is down or I'm not logged in, I don't want to assume that the value isn't set and continue on my merry way.

    Setting a package variable makes the most sense. However, I do see value in the throw/catch via eval idea. If something unexpected happens and the developer is asleep at the wheel, it is best to put the progam into full stop and not let the error grow until it does irreversible damage.

    The problem is I hate the "throw/catch" syntax because it breaks the flow of the program -- especially Perl's way of using eval to do the catching.

    An interesting possibility is to die -- unless the developer sets a package variable to allow a one time "don't die" call. If the developer wasn't paying attention, I die and stop the execution. If the developer sets the "don't die" flag, I can assume the developer is paying attention and wants to handle the error. In that case, I'll set the package variable.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://551690]
Approved by VSarkiss
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (3)
As of 2017-01-22 21:28 GMT
Find Nodes?
    Voting Booth?
    Do you watch meteor showers?

    Results (190 votes). Check out past polls.