7

アクションが終了する可能性のあるレキシカル ブロックの末尾にアクションを追加する機能が必要です。そして、例外が正常にスローされ、正常にキャッチされる必要があります。

残念ながら、メッセージに "(in cleanup)" を追加し、それらをトラップできないようにすることで、DESTROY 中に Perl の特殊なケースの例外が発生します。例えば:

{
    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 ブロッ​​クにラップすることは避けています。

4

2 に答える 2

2

これは意味的に正しいですか?私が理解していることから、これがあります(疑似コードで):

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

次の 2 つの可能性があります。

  • user_code()同じ実行をスローすることはありclean_up()ません。その場合、面白いガード ビジネスなしでシーケンシャル コードとして記述するだけで機能します。
  • user_code()そしてclean_up()、ある時点で、両方が同じ実行をスローする可能性があります。

両方の関数がスローされる可能性がある場合は、アクティブな例外が 2 つあります。現在スローされている複数のアクティブな例外を処理できる言語は知りませんがこれには十分な理由があると確信しています。Perl は(in cleanup)、例外を追加してトラップ不能にします。C++ 呼び出しterminate()Java は元の例外を黙ってドロップするなど。

との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;

または、何らかの方法で 2 つの例外を 1 つの例外オブジェクトに結合します。

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()思いますが、思いつきません。

要するに、両方の部分がスローされたときに何が起こるべきかを知らなければ、問題に答えることはできません.

于 2011-01-06T05:34:59.097 に答える
0

更新: エリックが指摘したように、以下のアプローチは書かれているようには機能しないようです!.

誰かがそれをうまく機能させることができる場合に備えて、私はこの答えを残しておきます。

問題は:

ローカル変数がスコープ外になったら、古いグローバル値をグローバルに関連付けられた変数に戻すには、FETCH/STORE の呼び出しが必要になると予想していましたが、どういうわけか、関連付けられたメカニズムにヒットすることなくサイレントに発生します (問題は例外処理とは関係ありません)。 .


Schwern - あなたのユースケースにネクタイテクニック ( Abigail による Perlmonks の投稿から盗んだもの) を使用できるかどうかは 100% 確信が持てません - これがあなたがやろうとしていたと私が思うことをしようとする私の試みです.

use Test::More tests => 6;

my $guard_triggered = 0;
sub user_cleanup { $guard_triggered++; die "En guarde!" }; # Line 4;
sub TIESCALAR {bless \(my $dummy) => shift}
sub FETCH     { user_cleanup(); }
sub STORE     {1;}
our $guard;
tie $guard => __PACKAGE__; # I don't think the actual value matters

sub x {
    my $x = 1; # Setup
    local $guard = "in x";
    my $y = 2; #user_code;
}

sub x2 {
    my $x = 1; # Setup
    local $guard = "in x2";
    die "you bastard"; #user_code;
}

ok !eval {
    x();
}, "the guard died";
is $@, "En guarde! at $0 line 4.\n",    "with the right error message";
is $guard_triggered, 1,                 "the guard worked";

ok !eval {
    x2();
}, "the guard died";
is $@, "En guarde! at $0 line 4.\n",    "with the right error message";
is $guard_triggered, 2,                 "the guard worked";

出力:

1..6
ok 1 - the guard died
ok 2 - with the right error message
ok 3 - the guard worked
ok 4 - the guard died
ok 5 - with the right error message
ok 6 - the guard worked
于 2011-01-06T02:14:36.327 に答える