Пакеты Perl: как импортировать классы в пространство имен пользователя? - PullRequest
1 голос
/ 26 февраля 2011

Я работаю над пакетом, который определяет исключения (используя Exception::Class::Nested) для своего «родительского» пакета. Я не хочу, чтобы родительский пакет использовал действительно длинные имена, хотя и Я не хочу загрязнять любое другое пространство имен.

Итак, я хотел бы экспортировать последний элемент имен классов в пространство имен пакета, use d пакета исключений.

Например, выдержка из пакета исключений:

package Klass:Foo::Bar::Exceptions;
use vars qw( @ISA @EXPORT @EXPORT_OK ... );
@ISA = qw( Klass::Foo::Bar Exporter );
use Exception::Class::Nested 0.04 (
    'Klass::Foo::Bar::Exceptions::BaseClass' => {
        description => 'Base class for exceptions',
        'Klass::Foo::Bar::Exceptions::NameError' => {
            error => "I don't like your face"
        }
    }
);

Пакет 'parent':

package Klass::Foo::Bar;
use Klass::Foo::Bar::Exceptions;
Klass::Foo::Bar::Exceptions::NameError->throw(error => "D'oh!");
my $e = NameError->new(error => 'Mwahaha!');

Я бы хотел бы экспортировать / импортировать класс исключений так, чтобы второй вызов (my $e один) работал так, как если бы NameError был определен в Klass::Foo::Bar, но я не разобрался пока.

(И прежде чем кто-нибудь скажет «но у Exception::Class есть изящная alias штука», я укажу, что псевдоним связан конкретно с методом throw исключения, поэтому я не может использовать это для неавтоматических new вызовов ..)

Одна вещь, которую я попробовал, это поместить ее в подпункт importer пакета исключений (@snames - это либо массив полностью определенных классов исключений ( например , 'Klass::Foo::Bar::Exceptions::NameError'), либо просто хвостовой конец ( например , 'NameError'):

my $caller = caller();
$caller ||= 'main';
my @snames = @{$EXPORT_TAGS{exceptions}};
for my $exc (@snames) {
    $exc =~ s/^.*:://;
    no strict qw(subs refs);
    *{"${caller}\:\:${exc}\:\:"} = \*{__PACKAGE__ . "\:\:${exc}\:\:"};
}

Но это заканчивается тем, что я должен вызывать исключения, используя Klass::Foo::Bar::NameError вместо NameError. Кажется, это работает, но слишком хорошо.

Я не хочу импортировать NameError в main::!

Боюсь,

Типглобы и таблицы символов все еще немного загадочны.

Я уверен, что есть способ сделать то, что я хочу (иначе я делаю то, чего не должен делать вообще, но давайте пока оставим это в покое). Кто-нибудь может мне помочь с этим?

Спасибо!

1 Ответ

5 голосов
/ 26 февраля 2011

В вашем примере import sub вы используете псевдонимы для пакетов, которые не будут выполнять то, что вы хотите.Вместо этого вы хотите создать подпрограммы с сокращенными именами, которые возвращают полное имя пакета:

sub import {
    my $caller = caller;
    for my $long (@{$EXPORT_TAGS{exceptions}}) { # for each full name
        my ($short) = $long =~ /([^:]+)$/;       # grab the last segment
        no strict 'refs';
        *{"$caller\::$short"} = sub () {$long};  # install a subroutine named 
                                                 # $short into the caller's pkg
                                                 # that returns $long
    }
}

Разбивая эту последнюю строку, sub () {$long} создает анонимную подпрограмму, которая не принимает аргументов.Ссылка на код содержит единственную переменную $long, которая сохраняет значение, которое она имела во время итерации цикла.Это называется лексическим замыканием, что в основном означает, что среда компиляции подпрограммы ($long и ее значение) будет сохраняться до тех пор, пока подпрограмма делает это.

Эта анонимная подпрограмма затем устанавливается в пакет вызывающего с$short имя.Полностью определенное имя подпрограммы в пакете вызывающей стороны: caller::subname, которую создает "$caller\::$short".Это тогда разыменовывается как typeglob *{ ... }.Присвоение глобусу типа со ссылкой заполняет этот слот глобуса.Таким образом, назначение ссылки на код устанавливает подпрограмму.

Другими словами, следующее объявление подпрограммы:

sub short () {'a::long::name'}

означает то же самое, что и:

BEGIN {*{__PACKAGE__.'::short'} = sub () {'a::long::name'}}
...