Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

The problem currently is like this, there is lot of different functions that wants different data and along the way they call other functions and so on. and i really need a fast way to tell when even one of them fails without changing too much code. as im trying find a solution that does not make things even more complicated than they already are, but still doing it most effective and reasonable way possible.

So after many hours of pondering, i can't really think a better way of doing this than those lovely goto labels everyone loves so much.

Here is a demonstration of the current solution.

#!/usr/bin/perl use strict; use warnings; my (%Numbers,$i); my @Values = qw/first second third/; $Numbers{$_} = ++$i for @Values; for my $cur (@Values) { my $value = &get_number ($cur); $value .= &get_ordinal ($cur); # more, more.. print "$cur is $value\n"; next; FAILED: print "WRONG: $cur, because: $@!\n"; next; } sub get_number { my $value = shift; if ($value eq "second") { &error("bad value"); } return $Numbers{$value}; } sub get_ordinal { return substr(shift, -2); } sub error { $@ = shift; goto FAILED; }

Just thought to ask if anyone ever dash against this kind of problem and found more elegant solution than the following. And yes i would rewrite it if it was up to me.

-Tauno, Thank you for your time.

Replies are listed 'Best First'.
Re: Jumping trought lot of subroutines
by GrandFather (Saint) on Jul 04, 2006 at 00:42 UTC

    By wrapping the potentially failing code up in an eval you can die and report errors back that way. Consider:

    use strict; use warnings; my (%Numbers,$i); my @Values = qw/first second third/; $Numbers{$_} = ++$i for @Values; for my $cur (@Values) { next if eval { my $value = &get_number ($cur); $value .= &get_ordinal ($cur); # more, more.. print "$cur is $value\n"; }; print $@; } sub get_number { my $value = shift; if ($value eq "second") { die "bad value"; } return $Numbers{$value}; } sub get_ordinal { return substr(shift, -2); }

    Prints:

    first is 1st bad value at noname.pl line 25. third is 3rd

    DWIM is Perl's answer to Gödel
Re: Jumping trought lot of subroutines
by imp (Priest) on Jul 04, 2006 at 07:17 UTC
    The approach GrandFather listed is a good one. One thing to keep in mind if you find that you like this approach is that a return inside the eval block returns from that block - not from the outer function.
    sub example { my $number = shift; eval { if (2 / $number > 3) { return 1; } }; print "Still here\n"; }
    To capture the value returned, you can do this:
    my $value = eval { # Do stuff; return 4; };
    And if you would like to explore a bit further you could look into some CPAN modules that provide exception objects, which help you to communicate the cause of the failure to listeners up the chain.

    I'm fond of Exception::Class, and you might find Exception::Class::TryCatch useful.

    And a discussion from perlmonks about Exception::Class - Exception::Class - how to use?

Re: Jumping trought lot of subroutines
by shmem (Chancellor) on Jul 04, 2006 at 10:46 UTC

    Disregard this post. Seems I havent got the OPs question right.

    That is what Carp is for:

    #!/usr/bin/perl use Carp qw(confess cluck); use strict; $SIG{__DIE__} = \&confess; $SIG{__WARN__} = \&cluck; a(); sub a { print "in a\n"; b("foo"); } sub b { warn "got wrong value $_[0]\n"; c("bar"); } sub c { print "in c\n"; die "dying because of $_[0]!\n"; }
    that gives
    in a got wrong value foo at foo.pl line 17 main::b('foo') called at foo.pl line 13 main::a() called at foo.pl line 9 in c dying because of bar! at foo.pl line 23 main::c('bar') called at foo.pl line 18 main::b('foo') called at foo.pl line 13 main::a() called at foo.pl line 9

    With that backtrace you know what's going wrong,and where.

    If you want to keep your code running and just examine the errors, you could write a sub that just stores the warnings in an array for later examination, and assign a reference of that to $SIG{__WARN__}.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}