Perl:如何遍历符号表以查找Foo::Bar的所有加载子类?
我有一个模块,它为它所属的包定义异常。使用Perl:如何遍历符号表以查找Foo::Bar的所有加载子类?,perl,exception,subclassing,symbol-tables,Perl,Exception,Subclassing,Symbol Tables,我有一个模块,它为它所属的包定义异常。使用Exception::Class::Nested声明异常 为了便于讨论,我们假设该模块名为Foo::Bar::Exception,它定义的所有异常都是该模块的一级子类(例如,Foo::Bar::Exception:DoNotDoThat)。我关心的所有异常都在这个模块文件中定义;我对任何其他模块所做的任何附加子类化都不感兴趣 对于我的import方法,我想构建一个定义的所有异常的列表,我想通过某种方式遍历符号表来实现,而不是保持一个硬编码的列表,该列表可
Exception::Class::Nested
声明异常
为了便于讨论,我们假设该模块名为Foo::Bar::Exception
,它定义的所有异常都是该模块的一级子类(例如,Foo::Bar::Exception:DoNotDoThat
)。我关心的所有异常都在这个模块文件中定义;我对任何其他模块所做的任何附加子类化都不感兴趣
对于我的import
方法,我想构建一个定义的所有异常的列表,我想通过某种方式遍历符号表来实现,而不是保持一个硬编码的列表,该列表可能与定义不同步,并且必须手动维护
那么,Foo::Bar::Exception->import
如何迭代Foo::Bar::Exception
的符号表来查找模块中声明的所有异常(第一级子类)?这只是我感兴趣的活动加载符号表;没有文件系统搜索等
谢谢
[增编]
由于我所有的异常子类名称都以exception
或Error
结尾,这看起来已经接近我想要的了:
my %symtable = eval("'%' . __PACKAGE__ . '::'");
my @shortnames = grep(m!(?:Error|Exception)::$!, keys(%symtable));
@shortnames = ( map { $_ =~ s/::$//; $_; } @shortnames );
my @longnames = ( map { __PACKAGE__ . '::' . $_ } @shortnames );
一些括号是不必要的,但我添加它是为了清楚数组上下文。用于
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};
}
}
}
在5.10之前版本的perls上启用API,否则将无法使用该API(尽管get_isarev
在5.10+上的速度要快得多,get_isarev
返回从命名类继承(直接或间接)的类,最后一个grep是因为get_isarev
是一个启发式函数——它永远不会错过继承您指定的类的类,但是在运行时@ISA
修改时,它可能会报告一个实际上不再继承您的类的类。因此,->isa
检查确保类仍然存在,并且仍然是一个子类
编辑:只注意到你只对名字空间下的包感兴趣的部分,但我仍然认为使用MRO API是找到它们的一个很好的基础——仅仅是在一个<代码> GRP/^ Fo::Bar::异常://< >代码>:< P>由于继承问题(显然是由
Exception::Class
或Exception::Class::Nested
引入的),我选择了纯符号表路由
长名称(例如,Foo::Bar::Exception:DoNotDoThat
)和短名称(DoNotDoThat
)都是可导出的;默认情况下会导出长名称。(不清楚是否有必要,但似乎没有什么害处。)
如果要导出短名称,则执行以下操作:
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 };
}
这与@Eric的答案非常接近,但在我看到他的答案之前就推导出来了
谢谢大家!我在
grep
上得到了一个无法调用未删节引用的方法“isa”
,所以@classes
中的某些内容是未删节的。这是Perl v5.10.1。刚刚检查:@classes
正在设置为空arrayref,[]
.Oops.缺少取消引用。已编辑。但如果为空,则继承设置不正确。继承应通过Exception::Class
或Exception::Class::Nested
进行设置,因为它们是实际创建包/类的对象。
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 };
}