如何在perl中动态加载模块和执行方法

时间:2011-06-29 18:33:35

标签: perl perl-module

我正在关注这个有关perl网络服务的question。我已经设法从主程序加载和执行模块。每个模块都是这样的:

#!/usr/bin/perl
package NiMbox::perlet::skeleton;

use strict;
use warnings;

require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(%DEFINITION main secondary);

our %DEFINITION;
$DEFINITION{'main'} = {
    summary => 'skeleton main',
    description => 'long skeleton main description',
    args => { 'box' => {}, 'other' => {} }
};
$DEFINITION{'secondary'} = {
    summary => 'skeleton secondary',
    description => 'long skeleton secondary description'
};

sub main {
    print "main...\n";
}

sub secondary {
    print "secondary...\n"
}

1; 

然后可以这样调用这些模块:

use NiMbox::perlet::skeleton;

my %DEFINITION = %NiMbox::perlet::skeleton::DEFINITION;
foreach my $s (keys %DEFINITION) {
    print "calling sub '$s'\n";
    NiMbox::perlet::skeleton->$s();
}

我如何摆脱NiMbox::perlet:skeleton的直接调用,使我能够做一些看起来像这样的事情(这不起作用,但说明我需要做什么):

my $perlet = 'skeleton';

use NiMbox::perlet::$perlet;

my %DEFINITION = %NiMbox::perlet::$perlet::DEFINITION;
foreach my $s (keys %DEFINITION) {
    print "calling sub '$s'\n";
    NiMbox::perlet::$perlet->$s();
}

由于我非常接近,我宁愿看到这个例子中缺少的东西,而不是使用另一个库。有什么想法吗?

2 个答案:

答案 0 :(得分:3)

如果您想使类名称动态化,您可以执行以下操作:

my $class = 'NiMbox::perlet::' . $perlet;
my $class_file = $class;
$class_file =~ s{::}{/};
$class_file .= '.pm';

require $class_file;
$class->import;

(或者甚至更好,使用Module::Load作为@Schwern suggests

获取%DEFINITION类有点棘手,因为它涉及符号引用。更好的方法是提供一个返回它的类方法,例如

package NiMbox::perlet::skeleton;
...
sub definition { 
    my %definition;
    $definition{main} = { summary => 'skeleton main', ... };
    return %definition;
}

然后你可以这样做:

my %DEFINITION = $class->definition;
foreach my $s( keys %DEFINITION ) { 
    print "calling sub '$s'\n";
    $class->$s;
}

答案 1 :(得分:3)

我相信你所寻找的是Exporter或许多关注的模块。我发现你已经在你的模块中使用它了,但是你没有用它来获取%DEFINITION。你这样做是这样的:

use NiMbox::perlet::skeleton qw(%DEFINITION);

foreach my $s (keys %DEFINITION) {
    print "calling sub '$s'\n";
    NiMbox::perlet::skeleton->$s();
}

%NiMbox::perlet::skeleton::DEFINITION别名为%DEFINITION并保存一堆打字。

为了能够使用%DEFINITION的变量定义,您可以使用“符号引用”来按名称引用变量......但这些都充满了危险。此外,导出全局变量意味着您在给定的命名空间中一次只能有一个变量。我们可以做得更好。

我建议的是将%DEFINITION哈希值更改为definition()类方法,该方法返回对%DEFINITION的引用。您可以返回哈希值,但引用可以避免浪费时间复制。

package NiMbox::perlet::skeleton;

use strict;
use warnings;

my %DEFINITION = ...;

sub definition {
    return \%DEFINITION;
}

现在您可以调用该方法并获取哈希引用。

use NiMbox::perlet::skeleton;

my $definition = NiMbox::perlet::skeleton->definition;

foreach my $s (keys %$definition) {
    print "calling sub '$s'\n";
    NiMbox::perlet::skeleton->$s();
}

动态地执行它,唯一的技巧是加载类。你可以eval "require $class" or die $@,但这有安全隐患。 UNIVERSAL::requireModule::Load可以更好地为您处理。

use Module::Load;

my $class = 'NiMbox::perlet::skeleton';
load $class;

my $definition = $class->definition;

foreach my $s (keys %$definition) {
    print "calling sub '$s'\n";
    $class->$s();
}