Perl 5: Проблемы с пространством имен при "использовании" модуля, сгенерированного 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
такой же, как и в первом случае, то есть sub Morpho::load
найден, несмотря на то, что не имеет префикса с пространством имен main::
, в которое он был загружен. Сравните это с поведением стандартного модуля, такого как Data::Dumper
- когда , что загружается перед объявлением пакета, sub Dumper
должен называться main::Dumper
, когда в пакете Qux::
.
1 ответ:
Итак, оказывается, что это ожидаемое поведение , как довольно лаконично объяснено в
perldoc perlmod
(Курсив мой):Пакеты могут сами содержать разделители пакетов, как в $OUTER::INNER:: var . Однако это ничего не говорит о порядке поиска имен. нет относительных пакетов : все символы либо локальны для текущего пакета, либо должны быть полностью определены от имени внешнего пакета вниз. Например, внутри нет ничего. пакет Внешний, что $внутренний::ВАР относится к $наружный:внутренний::ВАР. INNER относится к совершенно отдельному глобальному пакету.
Теперь все символы, экспортируемые модулем
Ufal::MorphoDiTa
, находятся внутри различныхподпространств (например, подпрограммаload
в пространстве именMorpho::
), поэтому они всегда должны быть полными (они не являются локальными для пакета, в который они импортируются). Что потенциально сбивает с толку, так это то, что префиксmain::
неявен для поиска символов, поэтому без пакета объявление (то есть в обычном скрипте), все работает нормально, потому что вызовMorpho::load
является ярлыком дляmain::Morpho::load
(и модуль действительно был загружен внутриmain::
).Но когда модуль импортируется в пакет
Это объясняет все очевидные причуды, упомянутые выше. Кончик шляпы добрым людям из проекта MorphoDiTa за их помощь и отзывчивость в решении этой головоломки!Qux::
,Morpho::load
должно быть указано какQux::Morpho::load
, потому что, перефразируя perldoc, "нигде в пакетеQux
нет того, чтоMorpho::load
относится кQux::Morpho::load
.Morpho
относится к совершенно отдельному глобальному пакету [main::Morpho
] " - который не существует, потому чтоMorpho::load
был загружен внутриQux::
, а не внутриmain::
.