如何在Perl/Moose中确定5.20+签名方法的元数?
在Moose/纯Perl中动态判断方法元数的解决方案
问题场景
use 5.036; # signatures use strictures; package Foo { use Moose; sub quux($self) { ... } } package Big::Quux { use Moose::Role; sub quux($self, $another) { ... } } package Bar { use Moose; extends 'Foo'; with 'Big::Quux'; } package main { my $class = rand > .5 ? 'Foo' : 'Bar'; say $class; $class->quux(); # 可能触发错误:"Too few arguments for subroutine 'Big::Quux::quux' (got 1; expected 2)" $class->quux('something quuxable'); # 可能触发错误:"Too many arguments for subroutine 'Foo::quux' (got 2; expected 1)" }
在上述场景中,需要根据运行时动态确定的类,调用quux方法时传入匹配其签名/元数的参数。$class->can('quux')能正确处理继承并返回对应代码引用,但纯Perl/Moose环境下,如何内省该方法的元数?此前尝试$class->meta->find_all_methods_by_name('quux')未解决问题,而Kavorka中这类内省操作较为简便。
解决方案
1. 结合Moose方法元对象与Sub::Util提取签名
利用Moose的方法元对象获取方法体,再通过Sub::Util的subsignature函数解析签名:
use Sub::Util qw(subsignature); my $class = rand > .5 ? 'Foo' : 'Bar'; say $class; my $method = $class->meta->get_method('quux'); if ($method) { my $sig = subsignature($method->body); if ($sig eq '($self, $another)') { $class->quux('something quuxable'); } elsif ($sig eq '($self)') { $class->quux(); } }
2. 用B模块解析子例程的参数数量
通过Perl核心的B模块直接解析OP树,获取方法的参数个数:
use B; my $class = rand > .5 ? 'Foo' : 'Bar'; say $class; my $code_ref = $class->can('quux'); if ($code_ref) { my $cv = B::svref_2object($code_ref); # PADLIST的MAX值表示参数总数(含$self) my $total_args = $cv->PADLIST->ARRAYelt(0)->MAX; if ($total_args == 2) { $class->quux('something quuxable'); } elsif ($total_args == 1) { $class->quux(); } }
3. 统一方法接口(推荐)
在Bar类中显式覆盖quux方法,提供兼容两种调用方式的统一接口,避免动态判断:
package Bar { use Moose; extends 'Foo'; with 'Big::Quux'; sub quux($self, $another = undef) { return defined $another ? $self->Big::Quux::quux($another) : $self->Foo::quux(); } }
修改后,main中可安全调用任意形式:
$class->quux(); $class->quux('something quuxable');
内容的提问来源于stack exchange,提问作者daxim
相关产品推荐
相关产品推荐

