当前包声明的子程序列表

List of subroutines current package declares

需要收集当前包本身声明的子例程列表 - 没有导入。
我看过 Package::Stash,但它列出了导入的名称(当然)。

想出了以下内容,但我不喜欢将包含内容移动到文件底部。

有人看到我如何收集相同的列表,但仍将我的包含内容保持在顶部附近吗?

package Foo;
use common::sense;
use Function::Parameters;
        # Must import at least "fun" and "method" first for them to work.
        # See bottom of file for rest of includes.


our %package_functions;

say join q{, }, sort keys %package_functions;


sub    foo_1    { ; }
fun    foo_2 () { ; }
method foo_3 () { ; }

BEGIN {
        # This block must be kept *after* the sub declarations, and *before* imports.
        no strict 'refs';
        %package_functions = map { $_ => 1 }                 # Hash offers more convenient lookups when/if checked often.
                grep { !/^(can|fun|method)$|^_/ }            # Exclude certain names or name patterns.
                grep { ref __PACKAGE__->can($_) eq 'CODE' }  # Pick out only CODEREFs.
                keys %{__PACKAGE__ . '::'};                  # Any functions above should have their names here.
}

use JSON;
use Data::Dumper;
# use ...

1;

输出(使用“perl”-E 'use Foo;'):

foo_1, foo_2, foo_3

如果 BEGIN 移动到另一个包含之后,我们会看到 Dumper、encode_json 等。

我的Devel::Examine::Subs可以做到这一点。查看允许您排除检索到的潜艇的方法(和 new() 的参数)的文档。

package TestLib;

use strict;
use warnings;
use feature 'say';

use Data::Dumper;    
use Devel::Examine::Subs;
use JSON;

my $des = Devel::Examine::Subs->new(file => __FILE__);
my $sub_names = $des->all;

say join ', ', @$sub_names;

sub one {}
sub two {}
sub three {}

输出:

perl -E 'use lib "."; use TestLib'

one, two, three

Deparse from core 完全可以做到这一点,所以你可以做 B::Deparse.pm 正在做的事情,即使用 B 模块来窥探 perl 的内部结构:

# usage: for_subs 'package', sub { my ($sub_name, $pkg, $type, $cv) = @_; ... }
sub for_subs {
    my ($pkg, $sub) = (@_, sub { printf "%-15s %-15s %-15s%.0s\n", @_ });
    use B (); no strict 'refs';
    my %stash = B::svref_2object(\%{$pkg.'::'})->ARRAY;
    while(my($k, $v) = each %stash){
        if($v->FLAGS & B::SVf_ROK){
            my $cv = $v->RV;
            if($cv->isa('B::CV')){
                $sub->($k, $pkg, sub => $cv);
            }elsif(!$cv->isa('B::SPECIAL') and $cv->FLAGS & B::SVs_PADTMP){
                $sub->($k, $pkg, const => $cv);
            }
        }elsif($v->FLAGS & B::SVf_POK){
            $sub->($k, $pkg, proto => $v->PV);
        }elsif($v->FLAGS & B::SVf_IOK){
            $sub->($k, $pkg, proto => '');
        }elsif($v->isa('B::GV')){
            my $cv = $v->CV;
            next if $cv->isa('B::SPECIAL');
            next if ${$cv->GV} != $$v;
            $sub->($k, $pkg, sub => $cv);
        }
    }
}

示例用法:

package P::Q { sub foo {}; sub bar; sub baz(){ 13 } }
for_subs 'P::Q';
sub foo {}; sub bar; sub baz(){ 13 }
for_subs __PACKAGE__;

应该导致:

foo             P::Q            sub
bar             P::Q            proto
baz             P::Q            sub
baz             main            const
for_subs        main            sub
bar             main            proto
foo             main            sub

如果您对 is not main 包感兴趣,您不关心空原型(如上例中的 bar),您只需要一个姓名列表,您可以将其剪切为:

# usage: @subs = get_subs 'package'
sub get_subs {
    my @subs;
    use B (); no strict 'refs';
    my %stash = B::svref_2object(\%{shift.'::'})->ARRAY;
    while(my($k, $v) = each %stash){
        next unless $v->isa('B::GV');
        my $cv = $v->CV;
        next if $cv->isa('B::SPECIAL');
        next if ${$cv->GV} != $$v;
        push @subs, $k;
    }
    @subs
}