2017-10-20 133 views
2

給定一個小Perl庫:(How)我可以在調用Symbol :: delete_package之後重新創建一個包嗎?

package P; 

use strict; 
use warnings; 

print("Loading P\n"); 

our $k1 = 'v1'; 
our $k2 = 'v2'; 
our $k3 = 'v2'; 

我試着寫一個程序加載,卸載和重裝包裝,以獲得更好的理解包在Perl是如何工作的:

# main.pl 
use strict; 
use warnings; 
use Symbol qw(delete_package); 

# Load module 
require "./P.pm"; 
my @incs = sort keys %INC; 
my $numSyms = keys %P::; 
print("Includes: @incs\nNumber of symbols: $numSyms\n"); 

# Unload module & delete package 
delete_package('P'); 
delete $INC{'./P.pm'}; 
@incs = sort keys %INC; 
$numSyms = keys %P::; 
print("Includes: @incs\nNumber of symbols: $numSyms\n"); 

# Load module again 
require "./P.pm"; 
@incs = sort keys %INC; 
$numSyms = keys %P::; 
print("Includes: @incs\nNumber of symbols: $numSyms\n"); 

運行此程序打印沿(其中該模塊通過keys %INC列出可能不同的順序)的路線的東西:

Loading P 
Includes: ./P.pm Exporter.pm Symbol.pm strict.pm warnings.pm 
Number of symbols: 4 
Includes: Exporter.pm Symbol.pm strict.pm warnings.pm 
Number of symbols: 0 
Loading P 
Includes: ./P.pm Exporter.pm Symbol.pm strict.pm warnings.pm 
Number of symbols: 0 

即似乎重新加載庫的方式按預期工作,但符號表%P::仍爲空。爲什麼圖書館第二次加載時不會重新填充?我試圖找到一種方法來重新加載模塊,而不使用任何CPAN軟件包。

回答

6

問題是%P::在編譯時被解析,所以它指的是glob delete_package被清除並導致符號表不符合。

因此當你用

keys %{ no strict qw(refs); \%{"P::"} }; 

keys %{ $::{"P::"} }; 

這意味着在更換

keys %P::; 

強制查找在運行時發生得到預期的輸出僅卸載一個包是不夠的;您需要卸載硬編碼引用的代碼以及從包中導入的代碼!

程序,刪除包(例如在快速CGI守護進程的腳本加載器)通常不硬編碼到他們刪除軟件包引用,所以他們通常不會遇到這個問題。以下是一個示例:

use strict; 
use warnings; 
use Symbol qw(delete_package); 

use FindBin qw($RealBin); 
use lib $RealBin; 

sub mod_path { 
    my ($mod_name) = @_; 
    return ($mod_name =~ s{::}{/}gr) . ".pm"; 
} 

sub load_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    require $mod_path; 
} 

sub unload_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    delete_package($mod_name); 
    delete($INC{$mod_path}); 
} 

sub get_package { 
    my ($pkg_name) = @_; 
    $pkg_name .= '::' if $pkg_name !~ /::\z/; 
    my $pkg = \%::; 
    $pkg = $pkg->{$_} for split /(?<=::)/, $pkg_name; 
    return $pkg; 
} 

sub dump_info { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    my $pkg = get_package($mod_name); 

    my $is_in_inc = grep { $_ eq $mod_path } keys %INC; 
    printf("Included: %s\n", $is_in_inc ? "yes" : "no"); 

    my $num_syms = keys(%$pkg); 
    print("Number of symbols: $num_syms\n"); 

    print("\n"); 
} 

for $mod_name ('P', 'P') { 
    load_module($mod_name); dump_info($mod_name); 
    # $mod_name->run(); 
    unload_module($mod_name); dump_info($mod_name); 
} 
+0

啊,真有意思!如果我有一個子程序'p'這是目前給予裁判的哈希(即它的調用像'P(\%P::);'),並在內部枚舉的符號,是有辦法內平移裁判'p'到一個字符串,這樣你的任何解決方案都可以使用?或者,我是否需要調整呼叫者,讓他們傳遞一個字符串? –

+0

調整調用者以將引用傳遞給正確的glob。 – ikegami

+0

謝謝;我想我必須閱讀在這種情況下'glob'的含義(我總是隻知道在文件名匹配的情況下)。 –

0

我收到ikegami代碼的內存泄漏。我使用了Test :: LeakTrace,它報告了一些問題。這裏是指找到內存泄漏稍微編碼:

#!/usr/bin/perl -w 
use strict; 
use lib './'; 
use Test::LeakTrace; 
use Symbol 'delete_package'; 

use FindBin qw($RealBin); 
use lib $RealBin; 

sub mod_path { 
    my ($mod_name) = @_; 
    return ($mod_name =~ s{::}{/}gr) . ".pm"; 
} 

sub load_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    require $mod_path; 
} 

sub unload_module { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    delete_package($mod_name); 
    delete($INC{$mod_path}); 
} 

sub get_package { 
    my ($pkg_name) = @_; 
    $pkg_name .= '::' if $pkg_name !~ /::\z/; 
    my $pkg = \%::; 
    $pkg = $pkg->{$_} for split /(?<=::)/, $pkg_name; 
    return $pkg; 
} 

sub dump_info { 
    my ($mod_name) = @_; 
    my $mod_path = mod_path($mod_name); 
    my $pkg = get_package($mod_name); 

    my $is_in_inc = grep { $_ eq $mod_path } keys %INC; 
    printf("Included: %s\n", $is_in_inc ? "yes" : "no"); 

    my $num_syms = keys(%$pkg); 
    print("Number of symbols: $num_syms\n"); 

    print("\n"); 
} 

leaktrace { 
    foreach my $mod_name ('P', 'P') { 
     load_module($mod_name); dump_info($mod_name); 
     unload_module($mod_name); dump_info($mod_name); 
    } 
} 

下面是輸出:

Included: yes 
Number of symbols: 1 

Included: no 
Number of symbols: 0 

Included: yes 
Number of symbols: 1 

Included: no 
Number of symbols: 0 

leaked SCALAR(0x556efcaf9a90) from /home/terry/projects/robinson/dev/trunk/command-line/experiments/perl-hacks/P.pm line 2. 
leaked SCALAR(0x556efcbb5628) from /usr/share/perl/5.26/Symbol.pm line 74. 

這 七歲表明,在Perl本身錯誤的問題。

這是否似乎仍然是一個錯誤?

相關問題