Как локализовать переменную в верхней области видимости в Perl? - PullRequest
6 голосов
/ 29 июля 2010

Я несколько раз сталкивался со следующей схемой при разработке модулей 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 ]

1 голос
/ 29 июля 2010

В документации по perlfunc для goto указано (выделено)

Форма goto-&NAME сильно отличается от других форм "goto". На самом деле, это вовсе не goto в обычном смысле, и у него нет стигмы, связанной с другими goto. Вместо этого он выходит из текущей подпрограммы (теряя все изменения, установленные local)

Какие проблемы с производительностью допускают косвенное обращение через автозагрузку, но не через оболочку?

1 голос
/ 29 июля 2010

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

Конечно, это не локализует переменную в родительской области, но я не думаю, что вы действительно захотите сделать это, даже если бы могли. Вы хотите локализовать $_ только на время разговора.

...