2010-01-19 129 views
9

前段時間,我問This question關於重寫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"); 

回答

20

這樣做的正確方法就是不要這樣做。找一些其他的方式來完成你在做什麼。這種技術具有全局變量的所有問題,平方。除非你完全正確地重寫了函數,否則你可能會破解你從來不知道存在的各種代碼。雖然你可能有禮貌不吹噓現有的重寫,但其他人可能不會。

覆蓋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()更改爲僅在真實系統之後運行的掛鉤。它獲取所有參數和退出代碼,它可以更改退出代碼,但不會更改實際執行的操作。在鉤子之前/之後添加是您可以做的唯一事情,如果您想要兌現現有的覆蓋。無論如何,它更安全一些。壓倒一切的系統現在是一個子程序,以防止BEGIN變得過於混亂。

您應該可以根據自己的需求進行修改。