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 解决这个难题的好心人致敬!
我的 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 解决这个难题的好心人致敬!