2

その一部であるパッケージの例外を定義しているモジュールがあります。例外はで宣言されていException::Class::Nestedます。

説明のために、このモジュールの名前Foo::Bar::Exceptionがであり、それが定義するすべての例外がそのモジュールの第1レベルのサブクラスであるとします(たとえばFoo::Bar::Exception:DoNotDoThat)。私が気にするすべての例外は、このモジュールファイルで定義されています。他のモジュールが行う追加のサブクラス化には興味がありません。

私のimport方法では、定義されているすべての例外のリストを作成したいと思います。定義と同期しなくなる可能性があり、手動で保守する必要があります。

では、のシンボルテーブルをFoo::Bar::Exception->import反復処理しFoo::Bar::Exceptionて、モジュールで宣言されているすべての例外(第1レベルのサブクラス)を見つけるにはどうすればよいでしょうか。これは、私が興味を持っているアクティブなロード済みシンボルテーブルです。ファイルシステム検索などはありません。

ありがとう!

[補遺]

私の例外サブクラス名はすべてExceptionまたはで終わるのでError、これは私が望むものに近づいているように見えます:

my %symtable = eval("'%' . __PACKAGE__ . '::'");
my @shortnames = grep(m!(?:Error|Exception)::$!, keys(%symtable));
@shortnames = ( map { $_ =~ s/::$//; $_; } @shortnames );
my @longnames = ( map { __PACKAGE__ . '::' . $_ } @shortnames );

括弧の一部は不要ですが、配列のコンテキストを明確にするために追加しました。

4

3 に答える 3

1

のシンボルテーブルFoo::Bar::Exception%Foo::Bar::Exception::であるため、次のように記述できます。

sub import {
    for my $key (keys %Foo::Bar::Exception::) {
        if (my ($name) = $key =~ /(.+)::$/) {
           my $pkg = 'Foo::Bar::Exception::'.$name;
           no strict 'refs';
           *{caller()."::$name"} = sub () {$pkg};
        }
    }
}
于 2011-03-07T01:46:25.430 に答える
1
use MRO::Compat;
my @classes = @{ mro::get_isarev("Foo::Bar::Exception") };
@classes = grep $_->isa("Foo::Bar::Exception"), @classes;

MRO :: Compatは5.10より前のperlsでmroAPIを有効にし(5.10以降でget_isarevはるかに高速ですが)、get_isarev指定されたクラスから(直接的または間接的に)継承するクラスを返します。最後のgrepは次の理由によるものです。get_isarevはヒューリスティックな種類の関数です。指定したクラスを継承するクラスを見逃すことはありませんが、実行時の@ISA変更に直面して、実際にはクラスを継承しなくなったクラスを報告する場合があります。したがって、->isaチェックにより、クラスがまだ存在し、サブクラスが残っていることが確認されます。

Edit: just noticed the part where you're only interested in packages that are under the namespace as well, but I still think that using the mro API is a good foundation for finding them -- just tack on a grep /^Foo::Bar::Exception::/ as well :)

于 2011-03-07T02:23:01.200 に答える
0

Due to the inheritance issues (apparently introduced by Exception::Class or Exception::Class::Nested), I've gone with the pure symbol-table route.

Both the longnames (e.g., Foo::Bar::Exception:DoNotDoThat) and the shortnames (DoNotDoThat) are exportable; the longnames are exported by default. (Unclear if that's necessary, but it seems to do no harm.)

If the shortnames are being exported, this does the trick:

my $caller = caller();
$caller ||= 'main';
my @snames = @{$EXPORT_TAGS{shortnames}};
for my $short (@snames) {
    my $exc = __PACKAGE__ . '::' . $short;
    no strict 'refs';
    *{"$caller\::$short"} = sub () { $exc };
}

which is quite close to @Eric's answer, but derived before I saw his.

Thanks, everyone!

于 2011-03-09T21:14:05.507 に答える