我有一段Perl代码有点像以下(强烈的简化):有一些级别的嵌套子程序调用(实际上是方法),一些内部函数执行自己的异常处理:
sub outer { middle() } sub middle { eval { inner() }; if ( my $x = $@ ) { # caught exception if (ref $x eq 'ARRAY') { print "we can handle this ..."; } else { die $x; # rethrow } } } sub inner { die "OH NOES!" }
现在我想更改代码,以便它执行以下操作:
>打印一个完整的堆栈跟踪,每一个例外,“气泡”一直到最外层(子外部).具体来说,堆栈跟踪不应该停在“eval {}”的第一个级别.
>不需要改变任何内部层次的实现.
现在,我这样做的方法是在外部子程序中安装一个本地化的__DIE__处理程序:
use Devel::StackTrace; sub outer { local $SIG{__DIE__} = sub { my $error = shift; my $trace = Devel::StackTrace->new; print "Error: $error\n","Stack Trace:\n",$trace->as_string; }; middle(); }
[编辑:我犯了一个错误,上面的代码实际上并不像我想要的那样工作,它实际上绕过了中间子句的异常处理.所以我猜这个问题应该是真的:我想要甚么行为吗?]
这是完美的,唯一的问题是,如果我正确地理解文档,它依赖于明确不赞成的行为,即__DIE__处理程序即使是在“eval {}”内也被触发的事实,他们真的不应该. perlvar和perlsub都表示在以后版本的Perl中可能会删除此行为.
有没有另一种方法可以实现这一点,而不依赖于弃用的行为,还是保存依赖,即使文档说不另外?
解决方法
依靠文档所说的任何东西都不推荐使用是不安全的.在未来的版本中,行为可能(并且可能会)会发生变化.依靠不推荐的行为会将您锁定到今天运行的Perl版本中.
不幸的是,我没有看到符合你的标准的方法. “正确”的解决方案是修改内部方法来调用Carp :: confess而不是死,并放置定制的$SIG {__ DIE__}处理程序.
use strict; use warnings; use Carp qw'confess'; outer(); sub outer { middle(@_) } sub middle { eval { inner() }; die $@ if $@ } sub inner { confess("OH NOES!") } __END__ OH NOES! at c:\temp\foo.pl line 11 main::inner() called at c:\temp\foo.pl line 9 eval {...} called at c:\temp\foo.pl line 9 main::middle() called at c:\temp\foo.pl line 7 main::outer() called at c:\temp\foo.pl line 5
由于您还在死亡,您可能不需要将呼叫陷入inner(). (你不在你的例子中,你的实际代码可能有所不同)
在您的示例中,您尝试通过$@返回数据.你不能这样做使用
my $x = eval { inner(@_) };