前一段时间,我问This question有关覆盖构建Perl函数的信息。

如何以允许多个替代的方式执行此操作?以下代码产生无限递归。

什么是正确的方法? 如果我重新定义一个函数,我不想踩别人的重定义。

package first;

my $orig_system1;
sub mysystem {
  my @args = @_;
  print("in first mysystem\n");
  return &{$orig_system1}(@args);
}

BEGIN {

  if (defined(my $orig = \&CORE::GLOBAL::system)) {
    $orig_system1 = $orig;
    *CORE::GLOBAL::system = \&first::mysystem;
    printf("first defined\n");
  } else {
    printf("no orig for first\n");
  }
}

package main;

system("echo hello world");

最佳答案

正确的做法是不要这样做。寻找其他方式来完成您的工作。该技术具有全局变量平方的所有问题。除非您完全正确地重写了该函数,否则您可能会破坏甚至不知道存在的所有代码。而且,尽管您可能会礼貌地不忽略现有的覆盖,但其他人可能不会。

覆盖system特别麻烦,因为它没有适当的原型(prototype)。这是因为它所做的事情在原型(prototype)系统中无法表达。这意味着您的覆盖无法完成system可以执行的某些操作。即...

system {$program} @args;

这是调用system的有效方法,尽管您需要阅读exec文档才能做到这一点。您可能会想“哦,好吧,那我就不会那样做”,但是,如果您使用的任何模块都执行此操作,或者它使用的任何模块都执行了操作,那么您就不走运了。

就是说,礼貌性地覆盖任何其他功能几乎没有什么不同。您必须捕获现有函数,并确保在新函数中调用它。是否在此之前或之后进行取决于您自己。

您的代码中的问题在于,检查函数是否已定义的正确方法是defined &function。即使没有定义函数,也要获取代码引用,否则始终会返回真实的代码引用。我不确定为什么,也许像\undef如何返回标量引用。有人猜测为什么调用此代码ref会使mysystem()无限递归。

还有一个额外的复杂性,就是您无法引用核心功能。 \&CORE::system不符合您的意思。您也无法通过符号引用来理解它。因此,如果您要调用CORE::system或现有的覆盖项(取决于定义的内容),则不能仅将一个或另一个分配给代码引用。您必须 split 逻辑。

这是一种方法。
package first;

use strict;
use warnings;

sub override_system {
    my $after = shift;

    my $code;
    if( defined &CORE::GLOBAL::system ) {
        my $original = \&CORE::GLOBAL::system;

        $code = sub {
            my $exit = $original->(@_);
            return $after->($exit, @_);
        };
    }
    else {
        $code = sub {
            my $exit = CORE::system(@_);
            return $after->($exit, @_);
        };
    }

    no warnings 'redefine';
    *CORE::GLOBAL::system = $code;
}

sub mysystem {
    my($exit, @args) = @_;
    print("in first mysystem, got $exit and @args\n");
}

BEGIN { override_system(\&mysystem) }

package main;

system("echo hello world");

请注意,我已将mysystem()更改为仅是在真实系统之后运行的钩子(Hook)。它获取所有参数和退出代码,并且可以更改退出代码,但不会更改system()实际执行的操作。如果要使用现有的覆盖,则只能执行在钩子(Hook)之前/之后添加操作。无论如何,它相当安全。困惑的覆盖系统现在位于子例程中,以防止BEGIN变得过于困惑。

您应该能够根据需要进行修改。

关于perl - 如何覆盖Perl函数,启用多个覆盖?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2096301/

10-16 05:36