Perl:动态加载模块并访问导出的内容

时间:2011-12-13 09:44:35

标签: perl dynamic module load package

require docs上研究Perl Stackoverflow和其他类似的链接之后,我仍然没有更聪明,一定要错过一个相当简单的技巧。我在运行时加载一个模块并在其中调用子程序。问题是我不一定知道导出的子例程的名称,但必须有一个并且它被导出。

模块都看起来像这样,即。他们粗略地遵循取自perlmonks.org

的模板
package modules::Test;

use strict;
use Exporter;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

$VERSION     = 1.00;
@ISA         = qw(Exporter);
@EXPORT      = (*TestSubSomeUnknownName);
@EXPORT_OK   = qw(&TestSubSomeUnknownName);
%EXPORT_TAGS = ( ALL => [qw(&TestSubSomeUnknownName)]
               );


sub TestSubSomeUnknownName
{
    # return a hash reference
}

然后我可以像这样访问sub,假设我知道它的名字:

use Module::Load;

my $package = "modules::Test";
my $subr = "TestSubSomeUnknownName";

load $package;
# Call the subroutine
my $hashref = $package->$subr;

但如果有人在包裹中拼写错误或我不知道该怎么办?解决方案似乎是使用其中一个EXPORT来查看其中的内容,但它是如何完成的?

4 个答案:

答案 0 :(得分:6)

  • @EXPORT应包含名称而不是符号。
  • 您不需要在功能名称之前使用&符号,它是非标准的。
  • 请记住在模块末尾返回1
  • 如果您想查看 >可以执行的操作包,请使用can

    die 'Auto-import sub was not named "TestSubSomeUnknownName"' 
        unless  $package->can( 'TestSubSomeUnknownName' )
        ;
    $package->TestSubSomeUnknownName(); 
    

答案 1 :(得分:2)

您可以尝试@modules::Test::EXPORT包含的内容。另外,请尝试运行$modules::Test::EXPORT[0]->();

答案 2 :(得分:1)

你是:

  • 编写模块并想知道如何导出子程序?
  • 您是否正在查看模块并希望使用其中的子程序?

对于第一个问题的答案,答案通常是不要自动导出任何内容。如果在@EXPORT_OK列表下放置子例程名称列表,则可以使用主程序中的以下语法导出这些子例程:

 use My::Module qw(subroutines to be imported);

如果要自动导入子例程,可以将其放在@EXPORT列表中。这些将自动导入File::Copy自动导入复制子程序。

现在,现代Perl标准在导出任何东西时都不赞成,因为它污染用户的命名空间而不必通知用户。如果您确实想要导入某些东西,那么标准现在要使用@EXPORT_OK,因此用户必须在use语句中列出要导入的子例程。这至少记录了污染。

File::Spec这样的现代模块一无所获。您要么必须在子例程前面加上模块的名称,要么使用面向对象的语法(即使像File::Spec那样,它也不是真正面向对象的,因为没有要定向的对象。)< / p>

这带来了另一件事:在模块中使用面向对象的Perl。然后,您不必担心导出任何内容,因为您没有在OOPerl中。

如果您正在尝试执行第二个问题并且只是尝试从第三方模块中查找子例程的名称,请使用perldoc命令并查看文档。您可以查看@EXPORT@EXPORT_OK列表以查看导出的内容,但可能有些内容未导出(例如$File::Find::Name模块中的File::Find变量)这可能很重要。

如果你真的想要沮丧和肮脏,你可以尝试浏览每个包变量:

#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);

while (my ($var, $type) = each %Foo::) {
    if (defined &$type) {
        say "$var is a subroutine";
    }
    else {
        say "$var is defined as something or another";
    }
}

package Foo;

our %bar = (foo => 1, bar => 2);
our @foo = qw(foo bar);
our $fubar = "barfu";

sub barfu {
    print "FOO!";
    return "FOO!";
}

这将通过包,显示已定义的内容,甚至告诉您它是否是子程序。我得到以下输出:

barfu is a subroutine
fubar is defined as something or another
bar is defined as something or another
foo is defined as something or another

我无法弄清楚如何查看它是什么类型的变量,而不是它是一个已定义的子程序。也许其他人可以帮助我搞清楚。我可能不得不对eval做些什么。


修改w /感谢Eric Storm

以下将显示其类型。不幸的是,这会说一切都是标量和全局。 Glob是可以理解的(它是一个glob)但是一个标量?我想现在就把这些类型留在for循环中。

#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use Data::Dumper;

while (my ($var, $type) = each %Foo::) {
    print "$var";
#   foreach my $ref_type (qw(SCALAR GLOB ARRAY HASH CODE REF LVALUE FORMAT IO VSTRING Regexp)) {
    foreach my $ref_type (qw(ARRAY HASH CODE REF LVALUE FORMAT IO VSTRING Regexp)) {
        if (defined *$type{$ref_type}) {
            say qq("$var" is a type $ref_type);
        }
    }
}

package Foo;

our %bar = (foo => 1, bar => 2);
our @foo = qw(foo bar);
our $fubar = "barfu";

sub barfu {
    print "FOO!";
    return "FOO!";
}

答案 3 :(得分:0)

所以最后我回到这个问题 - 也许我有点模糊,但我遇到的问题是动态加载模块,并通过查看动态加载的模块找出从这些模块导出的内容。

经过一段时间的黑客行为后,以下作品就是为了这个目的:

my $package = 'modules::Test';
load $package;
my $exports; 
eval {
    no strict;
    $exports = \@{"$package\::EXPORT"};
};
# Call the subroutine
my $subr = @{$exports}[0];
my $hash = $package->$subr if($package->can($subr));

要加载的包的名称只是一个字符串,包加载,$ exports行(从Exporter.pm复制,我发现单步执行$_->import)返回导出的符号列表和我然后可以拨打第一个。

如果有人能够进一步改善这一点,我很感兴趣。