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