如何在Perl的上部作用域中本地化变量?

Eri*_*rom 6 perl goto local uplevel

在开发使用AUTOLOAD或其他子例程调度技术的Perl模块时,我已经多次运行以下模式:

sub AUTOLOAD {
    my $self = $_[0];

    my $code = $self->figure_out_code_ref( $AUTOLOAD );

    goto &$code;
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,并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  
}
Run Code Online (Sandbox Code Playgroud)

caller由于性能和依赖性问题,涉及包装的解决方案是不可接受的.所以这似乎排除了第二种选择.

回到第一个,防止新值$_超出范围的唯一方法goto是不要本地化更改(不是可行的选项)或实现某种uplevel_localgoto_with_local.

我已经打得四处涉及各种排列PadWalker,Sub::Uplevel,Scope::Upper,B::Hooks::EndOfScope等人,但一直没能拿出来清理一个强大的解决方案$_在正确的时间,并且不换行caller.

有没有人发现在这种情况下有效的模式?

(SO问题:如何在不同的堆栈框架中本地化Perl变量?是相关的,但保留caller不是必需的,最终答案是使用不同的方法,因此在这种情况下解决方案没有帮助)

Mic*_*man 1

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;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

main c:\temp\foo.pl 6 - 2
Run Code Online (Sandbox Code Playgroud)

诚然,这并没有真正本地化父作用域中的变量,但我认为即使可以,您也不会真正想要这样做。您只想$_在通话期间进行本地化。