我遇到了 Perl 模块的命名空间问题。当我use
它在一个常规脚本文件中,所有公共(public)符号都被导入(隐式)main::
按预期打包。但是当我尝试 use
它在带有自己的包声明的源文件中(即通常是另一个模块),奇怪的事情开始发生。
有问题的模块可以在 CPAN 上找到,名称为 Ufal::MorphoDiTa
.它是一组 C++ 库的绑定(bind),使用 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 的一个怪癖,是 SWIG 上的一个错误事情的另一面(我没有足够的 Perl-Fu 来理解自动生成的绑定(bind)模块,该模块到处都有包声明),或者(另一个可能的替代方案)我自己的无知是否有问题。感谢您提供任何意见! :)
最佳答案
所以,原来这是预期行为 ,正如 perldoc perlmod
中非常简洁的解释(强调我的):
Packages may themselves contain package separators, as in $OUTER::INNER::var . This implies nothing about the order of name lookups, however. There are no relative packages: all symbols are either local to the current package, or must be fully qualified from the outer package name down. For instance, there is nowhere within package OUTER that $INNER::var refers to $OUTER::INNER::var. INNER refers to a totally separate global package.
现在,
Ufal::MorphoDiTa
导出的符号模块都位于不同的子命名空间中(例如 load
命名空间中的 Morpho::
子例程),因此它们必须始终是完全限定的(它们不是导入它们的包的本地)。可能令人困惑的是 main::
前缀对于符号查找是隐含的,因此没有包声明(即在常规脚本中),一切正常,因为调用 Morpho::load
是 main::Morpho::load
的快捷方式(并且该模块确实已加载到 main::
内)。但是当模块被导入包
Qux::
, Morpho::load
必须被称为Qux::Morpho::load
因为,用 perldoc 的话说,“包 Qux
中没有任何地方 Morpho::load
指的是 Qux::Morpho::load
。Morpho
指的是一个完全独立的全局包 [main::Morpho
]”——因为它不存在Morpho::load
被加载在里面 Qux::
,不在 main::
.这解释了上面引用的所有明显的怪癖。向 MorphoDiTa project 上的好人致敬为 their help and responsiveness在解决这个难题!
关于Perl 5 : namespace issues when `use` ing SWIG-generated module in declared package,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33703533/