【问题标题】:Perl prototype subroutine in class类中的 Perl 原型子例程
【发布时间】:2014-12-02 04:27:13
【问题描述】:

我试图在不实例化对象的情况下从类中调用原型函数。我班的一个例子MyClass

package MyClass;
use strict;
use warnings;

sub import{
        my $class = shift;
        my ($caller) = caller();
        eval "sub ${caller}::myprot(\&);";
        eval "*${caller}::myprot = \&MyClass::myprot;";        
}

sub myprot (&) {
    my ($f) = @_;
        $f->();
}

1;

我想从脚本main.pl调用原型:

use strict;
use warnings;

use MyClass;

myprot {
        print "myprot\n";
};

我得到了错误:

Use of uninitialized value in subroutine entry at MyClass.pm line 14.
Use of uninitialized value in subroutine entry at MyClass.pm line 14.
Undefined subroutine &main::myprot called at main.pm line 8.

我不太明白未定义的子例程错误:使用use,调用import,它定义了main.pl 的原型。我也真的不明白未初始化的值错误。 我很乐意得到一些解释。

【问题讨论】:

  • 我认为你需要 myprot (sub { print "myprot\n"}); 并且你还需要从你的 MyClass 包中导出 myprot 来做到这一点 stackoverflow.com/questions/17912400/…
  • @KeepCalmAndCarryOn:这不是有效的 Perl - 大概你的意思是使用大括号 {..} 而不是括号 (..)。不——他希望MyClass声明一个函数myprot,它将一个代码块作为它的唯一参数。但我同意他不应该让它看起来像一个没有sub 的子程序定义。
  • 我相信您不希望在实时代码中真正使用这个?我的回答解释了为什么它对你不起作用,但你真的不能在实验之外做任何类似的事情。
  • 不需要第一次评估;仅在自动加载或稍后将定义子例程时才需要前向声明,但在这里您将立即导出它。但请改用 Exporter
  • 您的错误消息来自这样一个事实,即您的 eval 在子例程表填充到调用者之前发生。查看the regarding documentation 并记住,据说use 大致等于BEGIN{require module; module::import(args)}。此外,您的evals 缺少一些反斜杠。

标签: perl class subroutine subroutine-prototypes


【解决方案1】:

您正在寻找出口商。

package MyClass;
use strict;
use warnings;

use Exporter qw( import );

our @EXPORT = qw( myprot );

sub myprot(&) {
    my ($f) = @_;
    $f->();
}

1;

我一般使用@EXPORT_OK(需要使用use MyClass qw( myprot );)而不是默认导出。

【讨论】:

  • 这很好用,谢谢!但我仍然不明白Exporter 的作用。例如,如果我不使用use,而是使用require MyClass;MyClass->import();,则执行import,但我不能调用myprot
  • 您正试图在声明myprot(及其原型)之前编译对myprot 的调用。 use MyClass; 基本上是 BEGIN { require MyClass; MyClass->import(); },因此您将导入移至运行时。
  • @user1981275 长话短说,Exporter 为您的模块提供了一个导入例程,可以满足您的大部分需求。如果您不了解useimport 如何交互,请不要编写自己的导入例程。 importusehow use works in modules 的文档可能有助于理解。 Prototypes 也是一个坑,没有扎实的理解就不要使用。
  • @ikegami 好的,现在对我来说很有意义。
【解决方案2】:

代码中有很多粗略的东西。

未经检查地使用 eval 意味着如果它失败了,你永远不会知道。 eval 应该用作eval "code" or die $@。你会发现它抛出了一个错误,因为当你弄乱符号表时 strict 不喜欢它(这就是 *name = \&code 正在做的事情)。

使用 eval 导出子例程是大材小用。 eval STRING 是一个潜在的安全漏洞,应该作为最后的手段使用(eval BLOCK 很好)。不用 eval 也可以操作符号表,但 strict 不喜欢使用符号引用。

my $caller = "foo";
*{"${caller}::myprot"} = \&MyClass::myprot;
# Can't use string ("foo::myprot") as a symbol ref while "strict refs" in use...

您必须先关闭严格。这通常称为“别名”。

no strict 'refs';
*{$caller.'::myprot'} = \&myprot;

不需要预先设置原型,别名会为你处理。

事实证明这一切都是不必要的,有许多模块可以为你做这件事。最常见的是Exporter,它随 Perl 一起提供。这使得您的自定义 import 变得不必要。

use Exporter 'import';
our @EXPORT = qw(myprot);

其他一般提示...

应避免在类中硬编码类的名称(即\&MyClass::myprot 应该只是\&myprot)。这使得更改类或移动代码变得更加困难。

不鼓励使用既是类又是导出函数的混合模块。它们更难使用、测试和记录并产生奇怪的副作用。您应该将myprot 放入它自己的模块中。

【讨论】:

  • 这是一个巧妙的总结。但我怀疑(希望)OP 是在玩 Perl 的神秘角落,没想到会在实时代码中使用它。
  • @Borodin 我对相反的情况很有信心。幸运的是,他们现在有了两个答案。 :)
  • Exporter 不需要继承,所以不要使用它:use Exporter 'import';,而不是use parent 'Exporter';
  • 不,导入被调用并且你的 reinvent-the-wheel-instead-of-just-using-Exporter 失败
  • @user1981275 我不能建议您避免使用一般的 eval 并避免重新发明 Exporter。它非常非常容易以微妙的方式出错,并且可能成为维护坑。编写自己的导入是有正当理由的,但这不是其中之一。
【解决方案3】:

你确定你真的要这样做吗?

问题是双引号会吃掉你在 glob 赋值中的反斜杠。

eval "*${caller}::myprot = \&MyClass::myprot;"

应该是

eval "*${caller}::myprot = \\&MyClass::myprot;"

但是请不要让我调试你的代码!

【讨论】:

    猜你喜欢
    • 2012-09-17
    • 2023-03-26
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 2019-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多