Perl:如何迭代符号表以查找 Foo::Bar 的所有加载子类?
我有一个模块,它正在为其所属的包定义异常。异常是用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 );
:括号是不必要的,但我添加它是为了清楚地了解数组上下文。
I have a module which is defining exceptions for the package of which it is a part. The exceptions are being declared with Exception::Class::Nested
.
For purposes of discussion, let's say that this module is named Foo::Bar::Exception
, and that all of the exceptions it defines are first-level subclasses of that (e.g., Foo::Bar::Exception:DoNotDoThat
). All of the exceptions I care about are defined in this module file; I'm not interested in any additional subclassing any other module does of me.
For my import
method, I want to construct a list of all the exceptions being defined, and I'd like to do it by traversing the symbol table somehow rather than keeping a hard-coded list that can get out of sync with the definitions and has to be manually maintained.
So, how can Foo::Bar::Exception->import
iterate through Foo::Bar::Exception
's symbol table to find all the exceptions (first-level subclasses) that have been declared in the module? It's just the active loaded symbol table I'm interested in; no filesystem searches or the like.
Thanks!
[addendum]
Since all of my exception subclass names end with Exception
or Error
, this looks like it's getting close to what I want:
my %symtable = eval("'%' . __PACKAGE__ . '::'");
my @shortnames = grep(m!(?:Error|Exception)::$!, keys(%symtable));
@shortnames = ( map { $_ =~ s/::$//; $_; } @shortnames );
my @longnames = ( map { __PACKAGE__ . '::' . $_ } @shortnames );
Some of the parenthesisation is unnecessary, but I added it for clarity about the array context.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Foo::Bar::Exception
的符号表是%Foo::Bar::Exception::
,所以你可以这样写:The symbol table for
Foo::Bar::Exception
is%Foo::Bar::Exception::
, so you could write:MRO::Compat 启用 mro API 在 5.10 之前的 perls 上,否则不会有它(尽管
get_isarev
在 5.10+ 上快得多),get_isarev
返回从指定类继承(直接或间接)的类,最终的 grep 是因为get_isarev
> 是一种启发式函数——它永远不会错过继承您指定的类,但面对运行时@ISA
修改,它可能会报告实际上不再继承您的类的类。因此,->isa
检查可确保该类仍然存在并且仍然是一个子类。编辑:刚刚注意到您只对命名空间下的包感兴趣,但我仍然认为使用 mro API 是查找它们的良好基础 - 只需添加还有
grep /^Foo::Bar::Exception::/
:)MRO::Compat enables the mro API on pre-5.10 perls that otherwise wouldn't have it (although
get_isarev
is much faster on 5.10+),get_isarev
returns classes that inherit (directly or indirectly) from the named class, and the final grep is becauseget_isarev
is a heuristic sort of function -- it will never miss a class that does inherit the one you specified, but in the face of runtime@ISA
modification it might report a class that actually doesn't inherit your class anymore. So the->isa
check makes sure that the class is still there and still a subclass.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 :)由于继承问题(显然是由 Exception::Class 或 Exception::Class::Nested 引入的),我选择了纯符号表路线。
长名称(例如、
Foo::Bar::Exception:DoNotDoThat
)和短名称(DoNotDoThat
)都是可导出的;默认情况下会导出长名称。 (不清楚这是否有必要,但似乎没有什么害处。)如果短名被导出,这就会起作用:
这与@Eric的答案非常接近,但在我看到他的答案之前就得出了。
谢谢大家!
Due to the inheritance issues (apparently introduced by
Exception::Class
orException::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:
which is quite close to @Eric's answer, but derived before I saw his.
Thanks, everyone!