【问题标题】:Perl 5: namespace issues when `use`ing SWIG-generated module in declared packagePerl 5:在声明的包中使用 SWIG 生成的模块时的命名空间问题
【发布时间】:2015-12-07 07:15:23
【问题描述】:

我遇到了 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 这样的标准模块的行为进行对比——当 that 在包声明之前加载时,子 Dumper 在包 @987654346 中时必须被称为 main::Dumper @。

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

【问题讨论】:

    标签: perl module namespaces package swig


    【解决方案1】:

    所以,事实证明这是预期的行为,正如perldoc perlmod 中非常简洁的解释(强调我的):

    包本身可能包含包分隔符,如 $OUTER::INNER::var 。然而,这并不意味着名称查找的顺序。 没有相关的包:所有符号要么是当前包的本地符号,要么必须是从外部包名开始的完全限定符。例如,在包 OUTER 中没有任何地方 $INNER::var 引用 $OUTER::INNER::var。 INNER 指的是一个完全独立的全局包。

    现在,Ufal::MorphoDiTa 模块导出的符号都存在于各种子命名空间中(例如,Morpho:: 命名空间中的load 子例程),因此它们必须始终是完全限定的(它们不是导入它们的包的本地)。可能令人困惑的是 main:: 前缀对于符号查找是隐式的,因此没有包声明(即在常规脚本中),一切正常,因为调用 Morpho::loadmain::Morpho::load 的快捷方式(和模块确实已加载到 main::)。

    但是当模块被导入包Qux::时,Morpho::load必须被称为Qux::Morpho::load,因为,套用perldoc,“包Qux中没有任何地方Morpho::load指的是@987654337 @.Morpho 指的是一个完全独立的全局包 [main::Morpho]"——它不存在,因为 Morpho::load 是在 Qux:: 中加载的,而不是在 main:: 中。

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

    【讨论】:

      猜你喜欢
      • 2013-04-15
      • 1970-01-01
      • 2011-04-11
      • 2015-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-19
      相关资源
      最近更新 更多