Я несколько раз сталкивался со следующей схемой при разработке модулей 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
не было обязательным требованием, и в конечном итоге ответ был на использование другого подхода так что это решение не поможет в этом случае)