如何覆盖Perl函数,启用多个覆盖?

时间:2010-01-19 19:14:26

标签: perl

前一段时间,我问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");

1 个答案:

答案 0 :(得分:20)

正确的做法是不要这样做。找到一些其他方法来完成你正在做的事情。这种技术具有全局变量的所有问题,平方。除非你完全正确地重写函数,否则你可能会破坏你从未知道的各种代码。虽然你可能礼貌地没有吹过现有的覆盖,但其他人可能不会。

覆盖system特别敏感,因为它没有合适的原型。这是因为它做了原型系统中无法表达的事情。这意味着您的覆盖不能执行system可以执行的某些操作。即...

system {$program} @args;

这是调用system的有效方式,但您需要阅读exec文档才能执行此操作。你可能会想“哦,我当时就不会这样做”,但是如果你使用的任何模块都是这样做的,或者它使用的任何模块都可以做到,那么你就不幸了。

也就是说,与礼貌地覆盖任何其他功能没什么不同。您必须捕获现有功能,并确保在新功能中调用它。无论你之前还是之后,都取决于你。

代码中的问题是检查函数是否已定义的正确方法是defined &function。使用代码引用,即使是未定义的函数,也将始终返回真正的代码引用。我不确定为什么,也许就像\undef将如何返回标量参考一样。为什么调用这个代码引用mysystem()导致无限递归是任何人的猜测。

还有一个额外的复杂性,你不能参考核心功能。 \&CORE::system没有按你的意思行事。你也不能用象征性的参考来得到它。因此,如果要根据定义的内容调用CORE::system或现有覆盖,则不能仅将一个或另一个分配给代码ref。你必须分裂你的逻辑。

这是一种方法。

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()更改为仅在真实系统之后运行的钩子。它获取所有参数和退出代码,它可以更改退出代码,但它不会更改system()实际执行的操作。如果要保留现有覆盖,则可以在挂钩之前/之后添加唯一的操作。无论如何它还是有点安全。最重要的系统现在是一个子程序,以防止BEGIN过于混乱。

您应该可以根据需要修改它。

相关问题