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

时间:2010-07-28 21:13:59

标签: perl local goto uplevel

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

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_localgoto_with_local

我参与了涉及PadWalkerSub::UplevelScope::UpperB::Hooks::EndOfScope和其他人的各种排列,但未能提供强大的功能解决方案,在正确的时间清理$_,并且不包裹caller

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

(SO问题:How can I localize Perl variables in a different stack frame?是相关的,但保留caller不是必需的,最终答案是使用不同的方法,因此在这种情况下解决方案没有帮助)

2 个答案:

答案 0 :(得分: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;
}

输出结果为:

main c:\temp\foo.pl 6 - 2

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

答案 1 :(得分:1)

goto的perlfunc文档指出(强调添加)

  

goto-&NAME形式与其他形式的“goto”完全不同。事实上,它根本不是正常意义上的结果,并且没有与其他结果相关的耻辱感。相反,它退出当前子例程(丢失local设置的任何更改) ...

什么样的性能问题允许通过自动加载进行间接,而不是通过包装器?