(How)我可以在调用Symbol :: delete_package之后重新创建一个包吗?
问题描述:
给定一个小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软件包。
答
问题是%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);
}
答
我收到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本身错误的问题。
这是否似乎仍然是一个错误?
啊,真有意思!如果我有一个子程序'p'这是目前给予裁判的哈希(即它的调用像'P(\%P::);'),并在内部枚举的符号,是有办法内平移裁判'p'到一个字符串,这样你的任何解决方案都可以使用?或者,我是否需要调整呼叫者,让他们传递一个字符串? –
调整调用者以将引用传递给正确的glob。 – ikegami
谢谢;我想我必须阅读在这种情况下'glob'的含义(我总是只知道在文件名匹配的情况下)。 –