in reply to Idomatic Handling of Subroutine Error

You're looking for some sort of throw/catch error-handling for special subs.
eval { some_sub() }; if ($@) { # Handle exception here } sub some_sub { die "Bad stuff happened" if $some_exception_happens; }
That's the way Perl does it.

Now, for a stylistic note - you shouldn't be doing the eval/$@ syntax for every single function call you make. Keep it for the critical parts of your system. In part, this is because of the overhead of eval. *shrugs*

Update: I stand corrected on the overhead. Thanks, btrott!

------
We are the carpenters and bricklayers of the Information Age.

Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Replies are listed 'Best First'.
Re: Re: Idomatic Handling of Subroutine Error
by btrott (Parson) on Sep 18, 2001 at 22:50 UTC
    That's not right. eval BLOCK has no overhead (or rather negligible overhead).

    eval EXPR (string eval) *does* have overhead, because the string needs to be compiled at runtime, but that's the issue here.

    In general, actually, I would tend to agree with you, but for a different reason: there is no sense in wrapping *every* function call in its own eval. Instead wrap an entire self-contained block in an eval, like this:

    eval { ... ... ... }; if ($@) { ## Got error }
    You will jump out of the block as soon as your code hits an exception, and you might as well keep your exception-handling in one place, rather than scattered all over the place.

    To the OP: check out Exception::Class and Error, among others.

Re: Re: Idomatic Handling of Subroutine Error
by dvergin (Monsignor) on Sep 18, 2001 at 22:58 UTC
    I have no doubt that this will work. But I'm looking for a way to hide the ugly details from the calling code.

    Perhaps (looking at your suggestion and making this up as I go...)

    sub my_sub { # do stuff eval { potential icky bit }; if ($@) { return 0; } else { return $something_useful; } } # MAIN unless ( $result = my_sub() ) { # use $@ to figure out what to do }
    Well and good. But what if the problem arises from the logic of my task and not from an eval-able error. Bad data for example.

    I'm willing to hid any ugliness in the sub. But how do I best set a retreavable value of my own chosing to flag the problem and still allow succinct, clean code where the sub is called:

    unless ( $result = my_sub() ) { # handle error condition based on error var }
      You're almost there.
      # MAIN unless ($result = my_sub() ) { # Now you use $result if it's not 0 } # WRAPPER function sub my_sub { eval { _my_sub(@_) }; if ($@) { return $@; } else { return ''; } } # Actual worker... sub _my_sub { # Do your stuff here. If something fails, do a die with a useful error + message. Otherwise, return 0. # For example ... die "Bad data passed in.\n"; }
      You actually do a die which doesn't end your script - it gets trapped by the eval and the string you passed die will be put in $@.

      Now, I personally dislike this type of error-handling because 0 is FALSE and non-zero is TRUE. Thus, you have to flip your thinking the way the C libs force you to and say you're calling the function and hope it "fails" for success. (Sorta like a drug screening ...)

      Instead, I would something similar. Instead of passing back '' for success, I'd pass back undef instead.

      # MAIN if (defined ($error = my_sub()) ) { # Now you use $error if it's defined } # WRAPPER function sub my_sub { eval { _my_sub(@_) }; if ($@) { return $@; } else { return undef; } } # Actual worker... sub _my_sub { # Do your stuff here. If something fails, do a die with a useful error + message. Otherwise, return 0. # For example ... die "Bad data passed in.\n"; }
      It seems like a semantic difference, but now your code use TRUE and FALSE the way they intuitively used, to indicate success and failure.

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Promising. But can I drop the wrapping sub by eval'ing the 'die' directly? I also need to have a useful returned val on success. With that, I'll need to reverse the return logic. Is there any hope for an approach like this?
        # MAIN unless ($result = my_sub($val1, $val2)) { my $err_str = $@; # handle error based on $err_str } # do wonderful things with $result sub my_sub { my ($param1, $param2) = @_; # do stuff if ($bad_data) { eval {die "bad data"}; # ??? return undef; } # do more stuff if ($useless_param2) { eval {die "useless param 2"}; # ??? return undef; } # finish doing stuff return $useful_result; }