Can I dynamically get a list of functions or function names from any Perl module?
Asked Answered
D

3

9

I would like to dynamically get a list of either function names (as strings) or function references from any arbitrary Perl module available on my system. This would include modules that may or may not have, e.g., a global @EXPORT_OK array in its namespace. Is such a feat possible? How does one pull it off if so?

Edit: From reading perlmod, I see that %Some::Module:: serves as a symbol table for Some::Module. Is this the correct place to be looking? If so, how can I whittle the table down to just the function names in Some::Module?

Dichromaticism answered 9/12, 2010 at 23:49 Comment(0)
K
8

You're on the right track. To wittle down the full symbol table to just the subs, something like this can be done (Hat tip "Mastering Perl", ch 8, for main package version of this):

use strict; # need to turn off refs when needed
package X;

sub x {1;};
sub y {1;};
our $y = 1;
our $z = 2;

package main;

foreach my $entry ( keys %X:: ) {
    no strict 'refs';
    if (defined &{"X::$entry"}) {
        print "sub $entry is defined\n" ;
    }
}

# OUTPUT
sub y is defined
sub x is defined
Kino answered 10/12, 2010 at 0:53 Comment(8)
Neat. I think I was getting close to this from studying the CPAN submission, Symbol::Table, but its ways are a little advanced for me.Purpura
This will fail if pasted into a file with use strict at the top. To prevent surprises, and to make it clear that this is an advanced technique, add no strict 'refs' before if (defined &{"X::$entry"}) {Nootka
@Narveson, that wouldn't work, and it's also not so dire. The X-package scalars just need to have our thrown in front of them.Purpura
@Nootka - U have a point - my assumption was that this was self contained. FixedKino
@DVK, did you try running the script again?Purpura
@BipedalShark - Duh. The sample package was not strict as originally written (the main package is actually fine). Made X package strict-safe.Kino
@BipedalShark - OK, NOW I noticed your "our" comment.Kino
@DVK, the no strict 'refs'; line is also unnecessary.Purpura
R
8

You may find this simple script handy:

#!/usr/bin/env perl
use strict;
use warnings;    
use Data::Dumper;

# dump of object's symbol table:
foreach my $className (@ARGV)
{
    print "symbols in $className:";

    eval "require $className";
    die "Can't load $className: $@" if $@;

    no strict 'refs';
    print Dumper(\%{"main::${className}::"});
}

But, if you're doing this in production code, I'd use Package::Stash instead:

my @subs_in_foo = Package::Stash->new('Foo')->list_all_symbols('CODE');
Rechaba answered 10/12, 2010 at 0:10 Comment(1)
Glad you said use strict and the top and no strict 'refs' where necessary.Nootka
K
8

You're on the right track. To wittle down the full symbol table to just the subs, something like this can be done (Hat tip "Mastering Perl", ch 8, for main package version of this):

use strict; # need to turn off refs when needed
package X;

sub x {1;};
sub y {1;};
our $y = 1;
our $z = 2;

package main;

foreach my $entry ( keys %X:: ) {
    no strict 'refs';
    if (defined &{"X::$entry"}) {
        print "sub $entry is defined\n" ;
    }
}

# OUTPUT
sub y is defined
sub x is defined
Kino answered 10/12, 2010 at 0:53 Comment(8)
Neat. I think I was getting close to this from studying the CPAN submission, Symbol::Table, but its ways are a little advanced for me.Purpura
This will fail if pasted into a file with use strict at the top. To prevent surprises, and to make it clear that this is an advanced technique, add no strict 'refs' before if (defined &{"X::$entry"}) {Nootka
@Narveson, that wouldn't work, and it's also not so dire. The X-package scalars just need to have our thrown in front of them.Purpura
@Nootka - U have a point - my assumption was that this was self contained. FixedKino
@DVK, did you try running the script again?Purpura
@BipedalShark - Duh. The sample package was not strict as originally written (the main package is actually fine). Made X package strict-safe.Kino
@BipedalShark - OK, NOW I noticed your "our" comment.Kino
@DVK, the no strict 'refs'; line is also unnecessary.Purpura
F
1

I'm using Perl 5.20. This works on my machine:

use strict;

package foo;
   our $some_var;
   sub func1 { return 'func1'}
   sub func2 { return 'func2'}

package main;
   sub callable {
      my ($x) = @_;
      return defined(&$x);
   }

   while (my ($k, $v) = each(%foo::)) {
      if (callable($v)) {
         print("$k\n");
      }
   }

   # output:
   # func1
   # func2
Faint answered 18/5, 2017 at 23:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.