4

Say I've got some bit of library code, not entirely unlike this:

sub try_hard {
  my $sub = shift;
  my $tries = 3;
  my @failures;
  while($tries--) {
    eval {
      my $success = $sub->(@_) or die "sub returned false value";
      1;
    } or do {
      push @failures, $@;
    }
  }
  die "try_hard: failed 3 times: $failures[-1]"
}

This might fail like this:

try_hard: failed 3 times: sub returned false value at BadUtils.pm line 7 

...which is not useful if you're calling try_hard in a number of places and you don't know which call was the one that soft-failed.

If I could get the source of $sub, I could change that die to:

  my $success = $sub->(@_) or die "sub returned false value at $file line $lineno
";

and the library would be slightly DWIMmer. Is it possible to achieve this?

badp
  • 11,409
  • 3
  • 61
  • 89
  • I realize that it's entirely possible for a coderef to be generated on the fly (e.g. `my $potato = eval "sub { $the_code }"`) and in those cases there might not be a good way to attribute paternity to the coderef. I'm happy to be right some of the time rather than none of the time, however. – badp Mar 26 '18 at 11:29
  • Could you use [`__LINE__`](https://perldoc.perl.org/functions/__LINE__.html) and [`__FILE__`](https://perldoc.perl.org/functions/__FILE__.html) ? – Håkon Hægland Mar 26 '18 at 11:35
  • @HåkonHægland wouldn't those be the `__LINE__` and the `__FILE__` of `try_hard`? checking... – badp Mar 26 '18 at 11:36
  • 1
    Yes I guess they would... Maybe you could try [`caller`](http://perldoc.perl.org/functions/caller.html) then? – Håkon Hægland Mar 26 '18 at 11:36
  • 1
    `__LINE__` does not work: https://paste.ubuntu.com/p/jFQdcbHjC4/ – badp Mar 26 '18 at 11:41

2 Answers2

3

Use croak instead of die. You can force a backtrace by starting your script using

perl -MCarp::Always script

or

PERL5OPT=-MCarp::Always script
ikegami
  • 367,544
  • 15
  • 269
  • 518
2

caller:

my ($package, $filename, $line, $subroutine) = caller(1);
my $success = $sub->(@_) or die "sub returned false value at ".
    "$filename line $line in sub $subroutine in package $package";

Also see: How can I get a call stack listing in Perl?

daxim
  • 39,270
  • 4
  • 65
  • 132
  • ...I guess that would be a useful approximation, if I can't know where `$sub` is I will at least know where the call to `try_hard` is. This will also be more useful should the one `$sub` be `try_hard`ed multiple times. – badp Mar 26 '18 at 11:44