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

发布于 2024-12-20 17:43:11 字数 1182 浏览 0 评论 0原文

在学习了 Perl require docs 以及 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 之一来查看其中的内容,但如何才能完成呢?

After studying the Perl require docs and other links like this on Stackoverflow I'm still none the wiser, must be missing a fairly simple trick. I'm loading a module at runtime and calling a subroutine in it. The problem is I don't necessarily know the name of the exported subroutine, but there must be one and it is exported.

The modules all look like this, ie. they roughly follow a template taken from 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
}

Then I can access the sub like this, assuming I know its name:

use Module::Load;

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

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

But what if someone mis-spelled the name in a package or I don't know it? The solution seems to be to use one of the EXPORTs to see what's in there, but how can it be done?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

习惯成性 2024-12-27 17:43:11
  • @EXPORT 应包含名称而不是符号。
  • 函数名称前不需要使用 & 符号,而且它是非标准的。
  • 请记住在模块末尾返回 1
  • 如果您想检查包可以做什么,请使用can

    die '自动导入子未命名为“TestSubSomeUnknownName”' 
        除非 $package->can( 'TestSubSomeUnknownName' )
        ;
    $package->TestSubSomeUnknownName(); 
    
  • @EXPORT should contain names not symbols.
  • You don't need the ampersand before the function name and it's non standard.
  • Remember the to return 1 at the end of a module.
  • If you want to check what a package can do, use can.

    die 'Auto-import sub was not named "TestSubSomeUnknownName"' 
        unless  $package->can( 'TestSubSomeUnknownName' )
        ;
    $package->TestSubSomeUnknownName(); 
    
一身软味 2024-12-27 17:43:11

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

You can try what @modules::Test::EXPORT contains. Also, try running $modules::Test::EXPORT[0]->();

贪了杯 2024-12-27 17:43:11

您是否:

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

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

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

如果要自动导入子程序,可以将其放在<代码>@EXPORT 列表。这些必须以 File::Copy 自动导入复制子例程的方式自动导入。

现在,现代 Perl 标准不赞成导出任何内容,因为它会污染用户的命名空间,而不必通知用户。如果您真的非常想导入某些内容,现在的标准是使用 @EXPORT_OK,因此用户必须在 use 语句中列出他们想要导入的子例程。这至少记录了污染情况。

一些现代模块(例如 File::Spec)不导入任何内容。您要么必须在子例程中添加模块名称前缀,要么使用面向对象语法(即使像File::Spec一样,它也不是真正面向对象的,因为没有)

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

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

如果您真的想深入了解一下,您可以尝试像这样遍历每个包变量:

#! /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 做一些事情。


感谢 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!";
}

Are you:

  • Writing a module and want to know how to export a subroutine?
  • Are you looking at a module and want to use a subroutine from it?

For the answer to the first question, the answer is usually Don't export anything automatically. If you put a list of subroutine names under @EXPORT_OK list, you can export these subroutines using the following syntax in the main program:

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

If you want to automatically import a subroutine, you can put it in the @EXPORT list. These will be automatically imported must the way File::Copy automatically imports the copy subroutine.

Now, modern Perl standards frown upon exporting anything because it pollutes the user's namespace without necessarily informing the user. If you really, really want to import something, the standard is now to use @EXPORT_OK, so the user has to list the subroutines they want to import in their use statement. This at least documents the pollution.

Some modern modules like File::Spec import nothing. You either have to prefix the subroutine with the name of the module or use object oriented syntax (even if like File::Spec, it's not really object oriented because there are no objects to orient.)

Which brings us to another thing: Use Object Oriented Perl in your modules. Then, you don't have to worry about exporting anything because you don't in OOPerl.

If you're trying to do the second question and simply are trying to find the names of the subroutines from a third party module, use the perldoc command and see the documentation. You could look at @EXPORT and the @EXPORT_OK list to see what is exported, but there might be things that aren't exported (like the $File::Find::Name variable in the File::Find module) that might be important.

If you really want to get down and dirty, you can try going through each package variable like this:

#! /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!";
}

This will go through the package, show you what's defined, and even tell you if it's a subroutine or not. I get the following output:

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

I wasn't able to figure out how to see what type of variable it is other than it's a defined subroutine. Maybe someone else can help me figure it out. I'd probably have to do something with eval.


Modification w/ Thanks to Eric Storm

The following will show the type it is. Unfortunately, this will say everything is a scalar and a glob. Glob is understandable (it is a glob) but a scalar? I guess just leave these types out of the for loop for now.

#! /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!";
}
ゞ花落谁相伴 2024-12-27 17:43:11

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

经过一番黑客攻击后,以下工作达到了目的:

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) 返回导出符号的列表,然后我可以调用第一个符号。

如果有人可以进一步改进这一点,我很感兴趣。

So finally I return to this question - perhaps I was a bit vague but the problem I have is to dynamically load modules, and find out what's exported from these modules by looking at the dynamically loaded module only.

After a bit of hacking the following works for the purpose:

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));

The name of the package to load is just a string, the package is loaded, the $exports line (copied from Exporter.pm that I found single stepping a $_->import) returns the list of exported symbols and I can then call the first one.

If someone can improve this still further I'm interested.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文