我遇到了Perl模块的命名空间问题。当我在常规脚本文件中use
时,所有公共符号都会按预期导入到(隐式)main::
包中。但是当我尝试在源文件中use
使用它自己的包声明(即通常是另一个模块)时,奇怪的东西开始发生。
有问题的模块可以在CPAN上找到Ufal::MorphoDiTa
。它是一组C ++库的绑定,使用SWIG自动生成。无需安装lib本身来重现下面的测试用例。
首先,一个没有包声明的常规脚本文件:
# script.pl
use Ufal::MorphoDiTa qw(:all);
use Data::Dumper;
# a closer look at symbols inside the main:: package
my %morph_in_main = %main::{ grep { /morph/i } keys %main:: };
print "main:: namespace:\n", Dumper \%morph_in_main;
# Morpho:: is exported by Ufal::MorphoDiTa
Morpho::load('foo');
正如预期的那样,来自Ufal::MorphoDiTa
的符号被导入main::
并调用Morpho::load
子例程(没有可见输出,但没关系):
$ perl script.pl
main:: namespace:
$VAR1 = {
'Morpho::' => *{'Ufal::MorphoDiTa::Morpho::'},
'_<morphodita/morphodita_perl.cpp' => *{'::_<morphodita/morphodita_perl.cpp'},
'_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle' => *{'::_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle'}
};
现在让我们添加一个包声明:
# Qux.pm
package Qux;
use Ufal::MorphoDiTa qw(:all);
use Data::Dumper;
# a closer look at symbols inside the main:: package
my %morph_in_main = %main::{ grep { /morph/i } keys %main:: };
print "main:: namespace:\n", Dumper \%morph_in_main;
# a closer look at symbols inside the Qux:: package
my %morph_in_qux = %Qux::{ grep { /morph/i } keys %Qux:: };
print "Qux:: namespace:\n", Dumper \%morph_in_qux;
# Morpho:: is exported by Ufal::MorphoDiTa
Morpho::load('foo');
正如你在下面看到的那样,在这种情况下,一些导入的符号会出现在main::
包中,而一些包含在声明的Qux::
包中(也许这是预期的行为?):
$ perl Qux.pm
main:: namespace:
$VAR1 = {
'_<morphodita/morphodita_perl.cpp' => *{'::_<morphodita/morphodita_perl.cpp'},
'Morpho::' => *{'::Morpho::'},
'_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle' => *{'::_</usr/local/Cellar/perl/5.22.0/lib/site_perl/5.22.0/darwin-thread-multi-2level/auto/Ufal/MorphoDiTa/MorphoDiTa.bundle'}
};
Qux:: namespace:
$VAR1 = {
'Morpho::' => *{'Ufal::MorphoDiTa::Morpho::'}
};
Undefined subroutine &Morpho::load called at Qux.pm line 11.
无论如何,如输出的最后一行所示, Perl突然找不到子程序。请注意我们所做的一切都是在所有use
语句之前添加包声明。
现在是最重要的 - 如果我们use Ufal::MorphoDiTa
之前声明package Qux
,一切都会重新开始:
# Qux.pm
use Ufal::MorphoDiTa qw(:all);
package Qux;
use Data::Dumper;
# etc.
使用perl Qux.pm
运行模块的输出与第一种情况相同,即找到子Morpho::load
,尽管没有以前缀为{加载它的main::
命名空间。将此与Data::Dumper
之类的标准模块的行为进行对比 - 当在包声明之前加载时,子Dumper
必须被称为main::Dumper
在包Qux::
时。
我很欣赏有关这里发生的事情的任何提示......这不是我不能解决它,但问题是困扰我 - 我不确定它是否是Perl的一个怪癖,一个bug事物的SWIG方面(我没有足够的Perl-Fu来理解自动生成的绑定模块,它在整个地方都有包声明),或者是否(另一个合理的替代方案)我自己的无知是错误的这里。感谢您的任何意见! :)
答案 0 :(得分:0)
所以,事实证明这是预期的行为,正如perldoc perlmod
中非常简洁地解释的那样(强调我的):
包本身可能包含包分隔符,如$ OUTER :: INNER :: var。然而,这并不意味着名称查找的顺序。 没有相关软件包:所有符号都是当前软件包的本地符号,或者必须从外部软件包名称中完全限定。例如,OUTER包中没有任何地方$ INNER :: var引用$ OUTER :: INNER :: var。 INNER指的是一个完全独立的全球包。
现在,Ufal::MorphoDiTa
模块导出的符号都存在于各种子名称空间中(例如load
命名空间中的Morpho::
子例程),所以它们必须始终是完全合格的(它们不是导入它们的包的本地)。可能令人困惑的是,main::
前缀是隐式的符号查找,因此没有包声明(即在常规脚本中),一切正常,因为调用Morpho::load
是{{1}的快捷方式(并且模块确实在main::Morpho::load
)中加载。
但是当模块导入到包main::
中时,Qux::
必须被称为Morpho::load
,因为,为了解释perldoc,“包裹Qux::Morpho::load
中没有任何地方Qux
引用Morpho::load
。Qux::Morpho::load
指的是一个完全独立的全局包[Morpho
]“ - 它不存在,因为main::Morpho
被加载到里面Morpho::load
,而不是Qux::
内。
这解释了上面提到的所有明显的怪癖。解决这个难题的MorphoDiTa project their help and responsiveness好人们的一角!