【问题标题】:Perl unexpected behavior: croak vs. try catchPerl 意外行为:croak 与 try catch
【发布时间】:2021-03-08 09:33:35
【问题描述】:

我看到了一些指向 catch 块本身(结束)的异常(参见下面的示例)。

在我看来,这是一个意想不到的行为,因为它改变了原始异常的位置并且难以调试(应该在第 13 行说死。)

如果我使用 die/confess 或使用 eval 而不是 try-catch,它会显示(正确的)第 13 行。

不知道我的代码将如何在堆栈中被调用,我现在开始避免使用 croak。你怎么看?我做对了吗,还是有办法改进?

最好的问候,史蒂夫

use Carp;
use Try::Tiny;

try {
  foo();
} 
catch {
  # do something before die
  die $_;
};             # this is line 10

sub foo {
  croak 'die'; # this is line 13
}

输出: die at line 10.

【问题讨论】:

  • 也许您希望 croak() 在您的 catch 块中,而 die() 在您的 foo{} 中?
  • @clamp,理想情况下是的,你是对的,但有时你正在开发 foo,你会想:嗯......如果输入 neq 'bla' 我必须发牢骚,但不知道以后会被捕获.
  • 如果您不介意切换模块,您可以按照我在reply 中的建议改用 Nice::Try,它会按预期工作。

标签: perl exception try-catch


【解决方案1】:

这是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 会指向检测到错误的位置,所以 库中,很可能对用户无用。但请阅读dieCarp 文档了解相关的复杂性。)

不明显的是,当 croak 在同一命名空间 (main::foo()) 中从 try-catch 在其自己的命名空间 (Try::Tiny) 中发出时,事情会变得混乱,并报告其语句的结尾.这可以通过在我上面的代码中添加foo() 并调用它(而不是模块中的子代码)来检查,我们可以重现问题的行为。

如果 main::foo()croak 内部是从 main:: 中的(复杂)语句调用的,则不会发生这种情况,因此这似乎是由于命名空间的 try-catch 混合。 (另一方面,try-catch 糖在调用堆栈中添加了一个匿名子,这肯定也会混淆。)

实际上,我想说:总是在模块之外使用croak(否则使用die),或者,如果你想模仿基于异常的代码,最好使用confess 和/或你的异常类层次结构。


甚至就像die ExceptionClass->new(...);

请记住,在例外情况下,Perl 只有孤独的dieeval。如需更多结构,您需要全部实现,或使用 Exception::ClassThrowable 等框架


通过编写和使用一种方法,该方法生成带有来自对象的有用信息的纯字符串,用于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而不是对一个命名子的引用。

【讨论】:

  • Re "所以在调用模块的sub的地方报错",并且调用者被认为是try-catch语句而不是foo()中的匿名潜艇。这有点奇怪。
  • @ikegami 是的,我将进一步研究这个问题(特别是通过添加到调用堆栈中的糖 try-catch 的作用)。在他们的示例中,我认为它将整个语句作为一个“行”,因此它不能单独报告 foo() 的块?在我的代码中它确实报告了它,也许因为它没有“内联”(在行数的意义上)调用Throw::pop
  • 它不仅仅是一个块;这是一个匿名潜艇。该代码相当于try(sub { foo(); }, catch(sub { ... }));。 (你可以从字面上使用它!)对foo() 的调用是它自己的语句,有它自己的行号。我认为这是因为croak 和对foo 的调用在同一个命名空间中,而try 不在。 perl -MCarp::Always ... 会确认这一点。
  • 是的,就是这样。 croak 实际上是用于从其他命名空间(直接或间接)调用的代码中。 try 正在从另一个命名空间调用匿名子这一事实正在搞砸。
  • 非常感谢@ikegami 的讨论和意见。如果只有 eval 的行为与 try catch 的行为相同(错误),那么我会同意避免在同一命名空间中使用 croak 的声明。不幸的是 eval 确实报告了正确的行。这种不一致会导致混淆使用什么。你们认为有可能纠正 try catch 吗?
【解决方案2】:

作为解决 OP 问题的一种方式,但是使用不同的模块,如果您使用 Nice::Try 代替,您将得到您期望的结果:

use Carp;
use Nice::Try;

try {
  foo();
} 
catch {
  # do something before die
  die $_;
}             # this is line 10

sub foo {
  croak 'die'; # this is line 13
}

你得到:

die at ./try-catch-and-croak.pl line 13.
    main::foo() called at ./try-catch-and-croak.pl line 4
    main::__ANON__ called at ./try-catch-and-croak.pl line 7
    eval {...} called at ./try-catch-and-croak.pl line 7    ...propagated at ./try-catch-and-croak.pl line 9.

为了全面披露,我是Nice::Try背后的作者

【讨论】:

    猜你喜欢
    • 2013-06-27
    • 1970-01-01
    • 2020-03-13
    • 2012-04-10
    • 1970-01-01
    • 1970-01-01
    • 2021-11-25
    • 2017-06-29
    • 2019-02-28
    相关资源
    最近更新 更多