这是预期的行为Carp https://perldoc.perl.org/Carp
[...] 使用carp()
or 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";
Prints
caught one: croak in module at exceptions.pl line 17.
die in catch: croak in module at exceptions.pl line 17.
如果子抛出使用die
那么这将被报告在line 11
,什么是正常行为die https://perldoc.perl.org/functions/die,以及您似乎所期望的。
If any of this is unclear or suboptimal then better use confess
and nicely get a full stacktrace. Also, if you wish more exception-based-like code behavior, can put together an exception/error class and throw its object,† designed and populated as desired.
如果你想confess
一个对象注意此时鲤鱼有极限 https://perldoc.perl.org/Carp#BUGS接着就,随即
The Carp
例程当前不处理异常对象。如果使用作为引用的第一个参数调用,它们只需调用die()
or warn()
, 作为适当的。
One way then would be to confess
a stringification of the object,‡ getting at least both a full stack backtrace and whatever is in the object.
我得到同样的行为eval
,通过替换 try-catch 和$_
above
eval {
Throw::bam();
};
if ($@) {
say "caught one: $@";
die "die in catch: $@";
};
打印与上面完全相同
虽然上面的内容很清楚并且行为符合预期,但在问题的示例中确实看到了奇怪的事情:错误是从整个 try-catch 语句报告的,即。在其右大括号处,即第 10 行所在的位置。 (这try
sub 是原型化的,整个 try-catch 是一个语法辅助,相当于调用try
这需要一个匿名潜艇,甚至更多。请参阅 ikegami 的评论和文档。另请参阅这个帖子 https://stackoverflow.com/a/57997918/4653379有关其语法的更多信息。)
这很奇怪,因为对嘎嘎声子的调用是foo()
在 - 的里面try
语句和这一行应该被报告,可以通过运行脚本来确认-MCarp::Always
。但在这个答案的代码中,调用的行Throw::bam
确实有报道——为什么会出现这种差异?
明确的目的croak
将在库中使用,以便用户可以看到他们(用户)代码中的哪个点以触发错误的方式调用了库。 (尽管die
会指向检测到错误的地方,所以in库,很可能对用户来说毫无用处。但读die
and Carp
相关复杂性的文档。)
不明显的是当croak
在同一名称空间中发出(main::foo()
) from try-catch
在它自己的命名空间中(Try::Tiny
)事情变得混乱,报告其声明结束。这可以通过添加一个来检查foo()
到我上面的代码并调用它(而不是模块中的子代码),我们就可以重现问题的行为。
这不会发生,如果main::foo()
with croak
inside 是从一个(复杂的)语句中调用的main::
,所以这似乎是由于命名空间的 try-catch 混合造成的。 (另一方面,try-catch 糖会向调用堆栈添加一个匿名子进程,这肯定也会把事情搞混。)
实际上,我会说:总是使用croak
超出模块(否则使用die
),或者,如果您想模仿基于异常的代码,最好使用confess
和/或您的异常类层次结构。
† Even just like die ExceptionClass->new(...);
请记住,在例外方面,Perl 只有孤独的die
, and eval
。对于更多结构,您需要全部实现,或者使用类似的框架异常::类 https://metacpan.org/pod/Exception::Class or 可投掷 https://metacpan.org/pod/Throwable
‡ By writing and using a method that generates a plain string with useful information from the object, for Carp::confess $obj->stringify
.
Or by 超载 https://perldoc.perl.org/overload the ""
类的(引用)运算符,因为它在以下情况下使用confess
-ing 一个对象(字符串上下文),对于Carp::confess $obj
;无论如何,这很好。
两者的基本示例:
use overload ( q("") => \&stringify );
sub stringify {
my $self = shift;
join ", ", map { "$_ => " . ( $self->{$_} // 'undef' ) } keys %$self
}
其中可以直接编写匿名子集,而不是对命名子集的引用sub
.