这是Carp 的预期行为
[...] 使用 carp() 或 croak() 将错误报告为来自调用模块的位置。 [...] 无法保证这就是错误所在,但这是一个有根据的猜测。
所以在调用模块的sub的地方报错,这就是用户想要的
use warnings;
use strict;
use feature 'say';
use Try::Tiny;
package Throw {
use warnings;
use Carp qw(croak confess);
#sub bam { die "die in module" }; # l.11
sub bam { croak "croak in module" };
1;
};
try {
Throw::bam(); # l.17
}
catch {
say "caught one: $_";
die "die in catch: $_";
};
say "done";
打印
抓到一个:在 exceptions.pl 第 17 行的模块中呱呱叫。
死在捕获中:在 exceptions.pl 第 17 行的模块中发出呱呱叫。
如果 sub 使用 die 进行抛出,那么这将在 line 11 报告,die 的正常行为是什么,以及您的预期。
如果其中任何一个不清楚或不理想,那么最好使用confess 并很好地获得完整的堆栈跟踪。此外,如果您希望更多基于异常的代码行为,可以将异常/错误类放在一起并抛出其对象,†根据需要进行设计和填充。
如果你想confess一个对象注意此时Carp has limits与那个
Carp 例程目前不处理异常对象。如果使用作为引用的第一个参数调用,它们只需调用die() 或warn(),视情况而定。
然后一种方法是 confess 对象的字符串化,‡ 至少获得完整的堆栈回溯和对象中的任何内容。
通过替换上面的 try-catch 和 $_,我得到了与 eval 相同的行为
eval {
Throw::bam();
};
if ($@) {
say "caught one: $@";
die "die in catch: $@";
};
和上面的打印完全一样
虽然上述内容很清楚并且行为符合预期,但在问题的示例中确实看到了一件奇怪的事情:错误是从整个 try-catch 语句中报告的,即。在它的右大括号处,即第 10 行所在的位置。 (try 子是原型的,整个 try-catch 是一种语法辅助,相当于对 try 的调用,它需要一个匿名子,然后可能更多。请参阅 ikegami 的评论和文档。另请参阅 this post更多关于它的语法。)
这很奇怪,因为在 try 语句中调用 croaking sub 是 foo() 并且应该报告这一行,这可以通过使用 -MCarp::Always 运行脚本来确认。但在这个答案的代码中,确实报告了对Throw::bam 的调用行——为什么会有这种差异?
croak 的明确目的是在库中使用,以便用户可以在他们的(用户)代码中看到他们以触发错误的方式调用库的位置。 (虽然die 会指向检测到错误的位置,所以在 库中,很可能对用户无用。但请阅读die 和Carp 文档了解相关的复杂性。)
不明显的是,当 croak 在同一命名空间 (main::foo()) 中从 try-catch 在其自己的命名空间 (Try::Tiny) 中发出时,事情会变得混乱,并报告其语句的结尾.这可以通过在我上面的代码中添加foo() 并调用它(而不是模块中的子代码)来检查,我们可以重现问题的行为。
如果 main::foo() 和 croak 内部是从 main:: 中的(复杂)语句调用的,则不会发生这种情况,因此这似乎是由于命名空间的 try-catch 混合。 (另一方面,try-catch 糖在调用堆栈中添加了一个匿名子,这肯定也会混淆。)
实际上,我想说:总是在模块之外使用croak(否则使用die),或者,如果你想模仿基于异常的代码,最好使用confess 和/或你的异常类层次结构。
† 甚至就像die ExceptionClass->new(...);
请记住,在例外情况下,Perl 只有孤独的die 和eval。如需更多结构,您需要全部实现,或使用 Exception::Class 或 Throwable 等框架
‡ 通过编写和使用一种方法,该方法生成带有来自对象的有用信息的纯字符串,用于Carp::confess $obj->stringify。
或者通过overloading 类的""(引号)运算符,因为它在confess-ing 对象(字符串上下文)时使用,对于Carp::confess $obj;无论如何,这很好。
两者的基本示例:
use overload ( q("") => \&stringify );
sub stringify {
my $self = shift;
join ", ", map { "$_ => " . ( $self->{$_} // 'undef' ) } keys %$self
}
哪里可以直接写一个匿名的sub而不是对一个命名子的引用。