gpt4 book ai didi

perl - 需要一个可以正常终止的词法作用域的结束

转载 作者:行者123 更新时间:2023-12-04 12:49:45 24 4
gpt4 key购买 nike

我需要能够将 Action 添加到 Action 可能会消失的词法块的末尾。而且我需要正常抛出异常并能够正常捕获。

不幸的是,在 DESTROY 期间,Perl 的特殊情况异常(exception)是通过在消息中添加“(清理中)”并使它们无法捕获。例如:

{
package Guard;

use strict;
use warnings;

sub new {
my $class = shift;
my $code = shift;
return bless $code, $class;
}

sub DESTROY {
my $self = shift;
$self->();
}
}

use Test::More tests => 2;

my $guard_triggered = 0;

ok !eval {
my $guard = Guard->new(
#line 24
sub {
$guard_triggered++;
die "En guarde!"
}
);
1;
}, "the guard died";

is $@, "En guarde! at $@ line 24\n", "with the right error message";
is $guard_triggered, 1, "the guard worked";

我希望它通过。目前,该异常完全被 eval 吞并了。

这是用于 Test::Builder2,所以我只能使用纯 Perl。

潜在的问题是我有这样的代码:
{
$self->setup;

$user_code->();

$self->cleanup;
}

即使 $user_code 死了,也必须进行清理,否则 $self 会进入一个奇怪的状态。所以我这样做了:
{
$self->setup;

my $guard = Guard->new(sub { $self->cleanup });

$user_code->();
}

复杂性是因为清理运行任意用户代码,这是该代码将死的用例。我希望该异常可以被守卫捕获并且不会改变。

由于改变堆栈的方式,我避免将所有内容都包装在 eval 块中。

最佳答案

这在语义上合理吗?据我了解,你有这个(伪代码):

try {
user_code(); # might throw
}
finally {
clean_up(); # might throw
}

有两种可能:
  • user_code()clean_up()永远不会投入相同的运行,在这种情况下,您可以将其编写为顺序代码而无需任何有趣的保护业务,它会起作用。
  • user_code()clean_up()可能在某个时候,两者都 throw 相同的运行。

  • 如果两个函数都可能抛出,那么您有两个事件异常。我不知道任何可以处理当前抛出的多个事件异常的语言,我相信这是有充分理由的。 Perl 添加 (in cleanup)并使异常无法捕获; C++ calls terminate() , Java drops the original exception silently等等等等。

    如果您刚从 eval 中走出来其中 user_code()cleanup()抛出异常,您希望在 $@ 中找到什么?

    通常这表示您需要在本地处理清理异常,可能是通过忽略清理异常:
    try {
    user_code();
    }
    finally {
    try {
    clean_up();
    }
    catch {
    # handle exception locally, cannot propagate further
    }
    }

    或者您需要选择一个异常以在两者都抛出时忽略(这是 DVK 的解决方案所做的;它忽略 user_code() 异常):
    try {
    user_code();
    }
    catch {
    $user_except = $@;
    }
    try {
    cleanup();
    }
    catch {
    $cleanup_except = $@;
    }
    die $cleanup_except if $cleanup_except; # if both threw, this takes precedence
    die $user_except if $user_except;

    或者以某种方式将两个异常合并为一个异常对象:
    try {
    user_code();
    }
    catch {
    try {
    clean_up();
    }
    catch {
    throw CompositeException; # combines user_code() and clean_up() exceptions
    }
    throw; # rethrow user_code() exception
    }
    clean_up();

    我觉得应该有办法避免重复 clean_up()上面例子中的行,但我想不出来。

    简而言之,如果不知道当两个部分都抛出时您认为会发生什么,您的问题就无法得到解答。

    关于perl - 需要一个可以正常终止的词法作用域的结束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4610445/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com