前一段时间,我问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");
答案 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过于混乱。
您应该可以根据需要修改它。