Как локализовать переменную в верхней области видимости в Perl?
Я несколько раз сталкивался со следующей схемой при разработке модулей Perl, которые используют AUTOLOAD
или другие методы отправки подпрограммы:
sub AUTOLOAD {
my $self = $_[0];
my $code = $self->figure_out_code_ref( $AUTOLOAD );
goto &$code;
}
Это отлично работает, и caller
видит правильную сферу.
Теперь то, что я хотел бы сделать, это установить локально $_
равно $self
во время исполнения &$code
, Что будет примерно так:
sub AUTOLOAD {
my $self = $_[0];
my $code = $self->figure_out_code_ref( $AUTOLOAD );
local *_ = \$self;
# and now the question is how to call &$code
# goto &$code; # wont work since local scope changes will
# be unrolled before the goto
# &$code; # will preserve the local, but caller will report an
# additional stack frame
}
Решения, которые включают упаковку caller
неприемлемы из-за проблем с производительностью и зависимостями. Так что, похоже, исключить второй вариант.
Возвращаясь к первому, единственный способ предотвратить новое значение $_
выходить за рамки во время goto
было бы либо не локализовать изменения (не жизнеспособный вариант) или реализовать какой-то uplevel_local
или же goto_with_local
,
Я играл со всеми видами перестановок, включая PadWalker
, Sub::Uplevel
, Scope::Upper
, B::Hooks::EndOfScope
и другие, но не смогли найти надежное решение, которое убирает $_
в нужное время и не оборачивает caller
,
Кто-нибудь нашел шаблон, который работает в этом случае?
(SO вопрос: как я могу локализовать Perl-переменные в другом фрейме стека? caller
не было обязательным требованием, и в конечном итоге ответ заключался в том, чтобы использовать другой подход, так что решение в этом случае бесполезно)
2 ответа
Sub::Uplevel, кажется, работает - по крайней мере, для простого случая, не включающего AUTOLOAD:
use strict;
use warnings;
use Sub::Uplevel;
$_ = 1;
bar();
sub foo {
printf "%s %s %d - %s\n", caller, $_
}
sub bar {
my $code = \&foo;
my $x = 2;
local *_ = \$x;
uplevel 1, $code;
}
Выход:
main c:\temp\foo.pl 6 - 2
Конечно, это на самом деле не локализует переменную в родительской области, но я не думаю, что вы действительно захотите сделать это, даже если бы могли. Вы хотите только локализовать $_
на время разговора.
Документация perlfunc для goto
указывает (акцент добавлен)
goto-&NAME
Форма довольно сильно отличается от других форм "гото". На самом деле, это не goto в обычном смысле этого слова, и у него нет стигмы, связанной с другими goto. Вместо этого он выходит из текущей подпрограммы (теряя все изменения, установленныеlocal
) …
Какие проблемы с производительностью допускают косвенное обращение через автозагрузку, а не через оболочку?