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::.

Я был бы признателен за любые указания относительно того, что здесь происходит... Дело не в том, что я не могу с этим справиться, но эта проблема не дает мне покоя-я не уверен, что это так. причуда Perl, ошибка на стороне SWIG вещей (у меня недостаточно Perl-Fu, чтобы понять автоматически генерируемый модуль Привязок, который имеет объявления пакетов повсюду), или (другая правдоподобная альтернатива) мое собственное невежество здесь виновато. Спасибо за любой вклад! :)
1 2

1 ответ:

Итак, оказывается, что это ожидаемое поведение , как довольно лаконично объяснено в perldoc perlmod (Курсив мой):

Пакеты могут сами содержать разделители пакетов, как в $OUTER::INNER:: var . Однако это ничего не говорит о порядке поиска имен. нет относительных пакетов : все символы либо локальны для текущего пакета, либо должны быть полностью определены от имени внешнего пакета вниз. Например, внутри нет ничего. пакет Внешний, что $внутренний::ВАР относится к $наружный:внутренний::ВАР. INNER относится к совершенно отдельному глобальному пакету.

Теперь все символы, экспортируемые модулем Ufal::MorphoDiTa, находятся внутри различныхподпространств (например, подпрограмма load в пространстве имен Morpho::), поэтому они всегда должны быть полными (они не являются локальными для пакета, в который они импортируются). Что потенциально сбивает с толку, так это то, что префикс main:: неявен для поиска символов, поэтому без пакета объявление (то есть в обычном скрипте), все работает нормально, потому что вызов Morpho::load является ярлыком для main::Morpho::load (и модуль действительно был загружен внутри main::).

Но когда модуль импортируется в пакет Qux::, Morpho::load должно быть указано как Qux::Morpho::load, потому что, перефразируя perldoc, "нигде в пакете Qux нет того, что Morpho::load относится к Qux::Morpho::load. Morpho относится к совершенно отдельному глобальному пакету [main::Morpho] " - который не существует, потому что Morpho::load был загружен внутри Qux::, а не внутри main::.

Это объясняет все очевидные причуды, упомянутые выше. Кончик шляпы добрым людям из проекта MorphoDiTa за их помощь и отзывчивость в решении этой головоломки!