Как я могу переопределить функции Perl, включив несколько переопределений? - PullRequest
9 голосов
/ 19 января 2010

некоторое время назад я задал Этот вопрос о переопределении сборочных функций 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 Ответ

20 голосов
/ 19 января 2010

Правильный способ сделать это - не делать это.Найдите другой способ выполнить то, что вы делаете.Эта техника имеет все проблемы глобальной переменной в квадрате.Если вы правильно не переписали функцию, вы могли бы сломать все виды кода, о котором вы даже не подозревали.И хотя вы можете быть вежливы, если не откажитесь от существующего переопределения, кто-то другой, вероятно, не будет.

Переопределение system особенно обидно, потому что у него нет подходящего прототипа.Это потому, что он делает вещи, которые невозможно выразить в системе-прототипе.Это означает, что ваше переопределение не может делать то, что может system.А именно ...

system {$program} @args;

Это правильный способ вызова system, хотя для этого нужно прочитать документы exec.Вы можете подумать: «Ну, тогда я просто не буду этого делать», но если какой-либо модуль, который вы используете, или любой другой, который он использует, делает это, то вам не повезло.

Этосказал, что есть немного отличается от вежливого переопределения любой другой функции.Вы должны перехватить существующую функцию и убедиться, что вызываете ее в своей новой.Делаете ли вы это до или после, зависит от вас.

Проблема в вашем коде состоит в том, что правильный способ проверить, определена ли функция, - defined &function.Взятие кода ref, даже неопределенной функции, всегда будет возвращать истинный код ref.Я не уверен, почему, может быть, это похоже на то, как \undef вернет скалярную ссылку.Почему при вызове этого кода ref ссылка 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().Добавление хуков «до / после» - единственное, что вы можете сделать, если хотите использовать существующее переопределение.Все равно немного безопаснее.Беспорядок в системе переопределения теперь находится в подпрограмме, чтобы предотвратить слишком запутанный НАЧАЛО.

Вы должны иметь возможность изменить это для своих нужд.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...