Perl 5:在声明的包中“使用”SWIG 生成的模块时出现命名空间问题

Perl 5: namespace issues when `use`ing SWIG-generated module in declared package

我的 Perl 模块有命名空间问题。当我在常规脚本文件中 use 时,所有 public 符号都按预期导入到(隐式)main:: 包中。但是当我尝试在源文件中 use 它有自己的包声明(即通常是另一个模块)时,奇怪的事情开始发生。

有问题的模块可以在 CPAN 上找到 Ufal::MorphoDiTa. It is a set of bindings to a C++ library, auto-generated using 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 等标准模块的行为进行对比——当 that 在包声明之前加载时,子 Dumper 必须被称为 main::Dumper 在包 Qux::.

我很感激任何关于这里发生的事情的指示...不是我不能解决它,而是这个问题困扰着我——我不确定它是否是 Perl 的一个怪癖, SWIG 方面的一个错误(我没有足够的 Perl-Fu 来理解自动生成的绑定模块,它到处都有包声明),或者(另一个似是而非的选择)我自己的无知是错在这里。感谢您的任何输入! :)

所以,事实证明这是 预期的行为 ,正如在 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 模块导出的符号都存在于各种 子命名空间 中(例如 Morpho:: 命名空间中的 load 子例程),因此它们必须始终是完全合格的(它们不是导入它们的包的本地)。可能令人困惑的是 main:: 前缀对于符号查找是隐含的,因此没有包声明(即在常规脚本中),一切正常,因为调用 Morpho::load 是 [=16 的快捷方式=](并且模块确实在 main:: 中加载)。

但是当模块被导入包 Qux:: 时,Morpho::load 必须被称为 Qux::Morpho::load 因为,用 perldoc 来解释,"there is nowhere within package Qux that Morpho::load refers to Qux::Morpho::load. Morpho refers to a totally separate global package [main::Morpho]" ——不存在,因为 Morpho::load 被加载到 Qux:: 中,而不是 main::.

这解释了上面提到的所有明显的怪癖。向 MorphoDiTa project for their help and responsiveness 解决这个难题的好心人致敬!