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

mmc*_*coo 9 perl

前段时间,我问过这个关于覆盖构建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");
Run Code Online (Sandbox Code Playgroud)

Sch*_*ern 20

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

覆盖system特别敏感,因为它没有合适的原型.这是因为它做了原型系统中无法表达的事情.这意味着你的覆盖不能做一些事情system.即...

system {$program} @args;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

这是一种方法.

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");
Run Code Online (Sandbox Code Playgroud)

请注意,我已经将mysystem()更改为仅仅是在真实系统之后运行的钩子.它获取所有参数和退出代码,它可以更改退出代码,但它不会改变system()实际操作.如果要保留现有覆盖,则可以在挂钩之前/之后添加唯一的操作.无论如何它还是有点安全.最重要的系统现在是一个子程序,以防止BEGIN过于混乱.

您应该能够根据需要修改它.