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

标签 perl module namespaces package swig

我遇到了 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::loadmain::Morpho::load 的快捷方式(并且该模块确实已加载到 main:: 内)。

但是当模块被导入包Qux:: , Morpho::load必须被称为Qux::Morpho::load因为,用 perldoc 的话说,“包 Qux 中没有任何地方 Morpho::load 指的是 Qux::Morpho::loadMorpho 指的是一个完全独立的全局包 [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/

相关文章:

node.js - Node 11 REPL 支持导入吗?

php - 在命名空间中定义 Controller 仍然 Class App\Http\Controllers 在 laravel 5.5 中发生错误

windows - 在 Perl 窗口中删除大量文本后如何添加新行?

perl - 在 Perl 中,如何导入示例 CSV,进行基本的文本操作,然后将其保存回 CSV?

perl - 如何计算文件中的所有字符,包括 Control 和 Unicode?

namespaces - 类库和命名空间有什么区别?

c# - Linq 和 localhost,实体命名空间与程序命名空间不同,但仍然出现错误

c++ - Perl Win32::API 和指针

php - 使用 Prestashop 1.6 在模块中获取产品类别名称

ruby-on-rails - 跨多个文件打破 ruby​​ 模块