Perl: динамическая загрузка модулей и доступ к экспортированному содержимому - PullRequest
2 голосов
/ 13 декабря 2011

После изучения Perl требуются документы и другие подобные ссылки на Stackoverflow Я до сих пор не знаю, должно быть, не хватает довольно простого трюка. Я загружаю модуль во время выполнения и вызываю в нем подпрограмму. Проблема в том, что я не обязательно знаю имя экспортируемой подпрограммы, но она должна быть, и она экспортируется.

Все модули выглядят так, т.е. они примерно соответствуют шаблону, взятому из perlmonks.org

package modules::Test;

use strict;
use Exporter;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

$VERSION     = 1.00;
@ISA         = qw(Exporter);
@EXPORT      = (*TestSubSomeUnknownName);
@EXPORT_OK   = qw(&TestSubSomeUnknownName);
%EXPORT_TAGS = ( ALL => [qw(&TestSubSomeUnknownName)]
               );


sub TestSubSomeUnknownName
{
    # return a hash reference
}

Тогда я могу получить доступ к сабвуферу, как это, если предположить, что знаю его имя:

use Module::Load;

my $package = "modules::Test";
my $subr = "TestSubSomeUnknownName";

load $package;
# Call the subroutine
my $hashref = $package->$subr;

Но что, если кто-то неправильно написал имя в пакете или я его не знаю? Похоже, что решение состоит в том, чтобы использовать один из экспортов, чтобы увидеть, что там, но как это сделать?

Ответы [ 4 ]

6 голосов
/ 13 декабря 2011
  • @EXPORT должен содержать имена не символы.
  • Вам не нужен амперсанд перед именем функции, и он нестандартный.
  • Не забудьте вернуть 1 в конце модуля.
  • Если вы хотите проверить, что пакет может сделать, используйте can.

    die 'Auto-import sub was not named "TestSubSomeUnknownName"' 
        unless  $package->can( 'TestSubSomeUnknownName' )
        ;
    $package->TestSubSomeUnknownName(); 
    
2 голосов
/ 13 декабря 2011

Вы можете попробовать то, что содержит @modules::Test::EXPORT.Также попробуйте запустить $modules::Test::EXPORT[0]->();

1 голос
/ 13 декабря 2011

Вы:

  • Написание модуля и хотите узнать, как экспортировать подпрограмму?
  • Вы смотрите на модуль и хотите использовать подпрограмму из него?

Для ответа на первый вопрос ответ обычно составляет Не экспортируйте ничего автоматически . Если вы поместите список имен подпрограмм в список @EXPORT_OK, вы можете экспортировать эти подпрограммы, используя следующий синтаксис в основной программе:

 use My::Module qw(subroutines to be imported);

Если вы хотите автоматически импортировать подпрограмму, вы можете поместить ее в список @EXPORT. Они будут автоматически импортированы так, как File::Copy автоматически импортирует подпрограмму копирования.

Теперь современные стандарты Perl не одобряются при экспорте чего-либо, потому что это загрязняет пространство имен пользователя без необходимости информировать пользователя. Если вы действительно, действительно хотите импортировать что-то, теперь стандарт должен использовать @EXPORT_OK, поэтому пользователь должен перечислить подпрограммы, которые он хочет импортировать, в своем операторе use. Это как минимум документирует загрязнение.

Некоторые современные модули, такие как File::Spec, ничего не импортируют. Вы должны либо поставить префикс подпрограммы перед именем модуля, либо использовать объектно-ориентированный синтаксис (даже если, как и File::Spec, он на самом деле не объектно-ориентирован, так как нет объектов для ориентации.)

Что подводит нас к другому: используйте объектно-ориентированный Perl в своих модулях. Тогда вам не нужно беспокоиться об экспорте чего-либо, потому что вы не работаете в OOPerl.

Если вы пытаетесь ответить на второй вопрос и просто пытаетесь найти имена подпрограмм из стороннего модуля, используйте команду perldoc и посмотрите документацию. Вы можете посмотреть @EXPORT и список @EXPORT_OK, чтобы увидеть, что экспортируется, но могут быть вещи, которые не экспортируются (например, переменная $File::Find::Name в модуле File::Find), которые могут быть важны.

Если вы действительно хотите потерпеть неудачу, попробуйте просмотреть каждую переменную пакета следующим образом:

#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);

while (my ($var, $type) = each %Foo::) {
    if (defined &$type) {
        say "$var is a subroutine";
    }
    else {
        say "$var is defined as something or another";
    }
}

package Foo;

our %bar = (foo => 1, bar => 2);
our @foo = qw(foo bar);
our $fubar = "barfu";

sub barfu {
    print "FOO!";
    return "FOO!";
}

Это пройдёт через пакет, покажет вам, что определено, и даже скажет вам, подпрограмма это или нет. Я получаю следующий вывод:

barfu is a subroutine
fubar is defined as something or another
bar is defined as something or another
foo is defined as something or another

Я не смог выяснить, как увидеть, к какому типу переменных это относится, кроме как к определенной подпрограмме. Может быть, кто-то еще может помочь мне понять это. Возможно, мне придется что-то делать с eval.


Модификация с Эриком Штормом

Следующий тип покажет, что это. К сожалению, это скажет, что все скаляр и шар. Глоб это понятно (это шар) но скаляр? Полагаю, пока просто оставьте эти типы вне цикла for.

#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use Data::Dumper;

while (my ($var, $type) = each %Foo::) {
    print "$var";
#   foreach my $ref_type (qw(SCALAR GLOB ARRAY HASH CODE REF LVALUE FORMAT IO VSTRING Regexp)) {
    foreach my $ref_type (qw(ARRAY HASH CODE REF LVALUE FORMAT IO VSTRING Regexp)) {
        if (defined *$type{$ref_type}) {
            say qq("$var" is a type $ref_type);
        }
    }
}

package Foo;

our %bar = (foo => 1, bar => 2);
our @foo = qw(foo bar);
our $fubar = "barfu";

sub barfu {
    print "FOO!";
    return "FOO!";
}
0 голосов
/ 13 января 2012

Итак, наконец, я возвращаюсь к этому вопросу - возможно, я был немного расплывчатым, но у меня проблема в том, чтобы динамически загружать модули и выяснять, что экспортируется из этих модулей, просматривая только динамически загружаемый модуль.

После небольшого взлома с этой целью работает следующее:

my $package = 'modules::Test';
load $package;
my $exports; 
eval {
    no strict;
    $exports = \@{"$package\::EXPORT"};
};
# Call the subroutine
my $subr = @{$exports}[0];
my $hash = $package->$subr if($package->can($subr));

Имя загружаемого пакета - просто строка, пакет загружен, строка $ exports (скопированная из Exporter.pm, котораяЯ обнаружил, что пошаговое выполнение $ _-> import ) возвращает список экспортированных символов, и я могу затем вызвать первый из них.

Если кто-то может улучшить это, я заинтересован.

...