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

时间:2015-11-13 23:52:12

标签: perl module namespaces package swig

我遇到了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来理解自动生成的绑定模块,它在整个地方都有包声明),或者是否(另一个合理的替代方案)我自己的无知是错误的这里。感谢您的任何意见! :)

1 个答案:

答案 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::loadQux::Morpho::load指的是一个完全独立的全局包[Morpho]“ - 它不存在,因为main::Morpho被加载到里面Morpho::load,而不是Qux::内。

这解释了上面提到的所有明显的怪癖。解决这个难题的MorphoDiTa project their help and responsiveness好人们的一角!