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

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

Другие вопросы по тегам