【问题标题】:Make perl look ahead for sub prototypes让 perl 向前看子原型
【发布时间】:2018-12-30 03:18:30
【问题描述】:

Perl 有点太宽容了:如果你将额外的参数传递给subs,它们就会被忽略。

为避免这种情况,我想使用原型来确保每个sub 都被赋予了正确数量的参数。

只要我在使用原型之前声明它就可以了:

sub mysub($);
sub mysub2($);

mysub(8);
mysub(8,2); # Complain here                                                            

sub mysub($) {
    mysub2($@);
}

sub mysub2($) {
    if($_[0] == 1) {
        mysub(2);
    }
    print $@;
}

但我真的很讨厌把它分开。我宁愿 Perl 阅读完整的文件,看看是否有进一步的声明。所以我想写这样的东西:

use prototypes_further_down; # This does not work

mysub(8);
mysub(8,2); # Complain here                                                            

sub mysub($) {
    mysub2($@);
}

sub mysub2($) {
    if($_[0] == 1) {
        mysub(2);
    }
    print $@;
}

我能以某种方式让 Perl 这样做吗?

【问题讨论】:

  • 从根本上说,你所问的都是不可能的。原型不添加参数检查;原型更改子调用的解析规则。因此,在进行子调用之前必须知道存在。
  • 你为什么把你的 sub decls 放在首位?这只是邀请范围泄漏。因此,无论是您询问的原因还是为了防止 subs 捕获他们不应该捕获的 var,请将您的 subs 放在您的代码之前。
  • 那又怎样?在极少数情况下使用前向声明。
  • 如果你想要(一些相似的)参数检查,切换到使用“正确的”子例程签名。当使用无效数量的参数调用子例程时,这些会死掉(在运行时)。 perldoc.perl.org/perlsub.html
  • 如果你不知道,从 v5.20 开始就有subroutine signatures,就是为了这个目的。有一个深入的讨论in Effective Perler。它们是实验性的,如果这会扼杀交易,那么我认为您将按照 Perl 的设计精神进行手动检查。有一些模块可以帮助检查(但对我来说,它们 add 有效)。原型并不是你想要的。

标签: perl function-prototypes


【解决方案1】:

为避免这种情况,我想使用原型来确保每个sub 都被赋予了正确数量的参数。

不,你不会。尽管名称相似,Perl 原型不是你父亲的函数原型。引用The Problem with Prototypes(强调我的),

Perl 5 的原型有两个用途。首先,它们提示解析器更改解析子例程及其参数的方式。其次,它们改变了 Perl 5 在执行这些子例程时处理这些子例程的参数的方式。 一个常见的新手错误是假设它们与其他语言中的子例程签名具有相同的语言目的。这不是真的。

除了它们没有相同的预期目的之外,绕过原型是微不足道的,因此它们无法为故意以(您认为的)“错误”方式调用您的代码的人提供实际保护。正如perldoc perlsub 告诉我们的那样,

函数声明必须在编译时可见。原型仅影响对函数的新式调用的解释,其中新式被定义为不使用& 字符。换句话说,如果你把它当作一个内置函数来调用,那么它的行为就像一个内置函数。如果您将其称为老式子例程,那么它的行为就像一个老式子例程。很自然地,原型对子程序引用(如\&foo)或间接子程序调用(如&{$subref}$subref->())没有影响。

方法调用也不受原型影响,因为要调用的函数在编译时是不确定的,因为调用的确切代码取决于继承。

即使你可以让它抱怨mysub(8,2)&mysub(8,2)$subref = \&mysub; $subref->(8,2) 或(如果mysubpackage MyModule 中的对象方法)$o = MyModule->new; $o->mysub(8,2) 也不会抱怨。

如果您想验证如何使用核心 Perl(5.20 之前)调用您的子程序,那么您需要自己在子程序的主体中执行验证。 Perl 5.20 和更新版本有一个(在撰写本文时为“实验性”)Signatures 子声明扩展,它可能适用于您的目的,但我自己从未使用过它,所以我无法谈论它的有效性或限制。还有许多CPAN 模块可用于处理此类事情,您可以通过搜索“签名”或“原型”等内容来找到它们。

无论您选择哪种方法,除非您在使用这些签名之前定义它们,否则您将无法获得有关不正确函数签名的编译时错误。在您的示例中,两个子相互调用,这可以通过使用前向声明提前建立其签名来完成:

sub mysub($foo);                               # Forward declaration
sub mysub2 { mysub(8) }
sub mysub  { mysub2('infinite loops ftw!') }   # Complete version of the code

【讨论】:

  • 实验性子程序签名确实会进行参数计数检查。 Function::Parameters 是一个类似的扩展,可在 Perl 5.14+ 上使用,另外还可以执行 Type::Tiny 类型检查。
猜你喜欢
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 2011-12-29
  • 1970-01-01
  • 1970-01-01
  • 2015-04-19
  • 1970-01-01
  • 2012-09-17
相关资源
最近更新 更多