【问题标题】:How to you distinguish a symbol table from a regular hash variable?如何区分符号表和常规哈希变量?
【发布时间】:2011-03-01 21:31:20
【问题描述】:

有什么方法可以判断散列引用是否指向符号表?

也就是,怎么会有函数

sub foo {
    my ($hashref) = @_;
    ...
}

知道它是否被调用为

foo( \%main:: )

而不是

foo( \%main )

该应用程序是一个函数,有时会处理 tie 的哈希变量,但我想避免尝试绑定符号表。

【问题讨论】:

    标签: perl glob


    【解决方案1】:

    看起来这可以通过 C API 使用 HvNAME 完成。以下来自perlapi

    HvNAME

    返回一个存储的包名, 如果 stash 不是 stash,则为 NULL。看 SvSTASH , CvSTASH .

    1. char* HvNAME(HV* stash)

    【讨论】:

    • 我刚刚开始通过运行perl -MDevel::Peek -e 'Dump(\%a::),Dump(\%a)' 推断出这一点。感谢您的链接,这样我就可以减少货物崇拜。如果没有 XS,您仍然可以执行 $is_a_symtable = do { $sv=B::svref_2object($hashref);ref($sv) eq 'B::HV' && $sv->NAME}; 之类的操作
    【解决方案2】:

    您可以查找以 '::' 结尾的键,这表明它有其他包,或者所有值都是符号引用。

    • 当然,即使在这里,也很难从仅存储符号的散列中区分一个存储(无论出于何种原因)。我正在研究B::svref_2object,但即使是存储在常规哈希中的隐藏符号也会为$sym->can( 'STASH' ) 返回一些东西。

    我认为您可能会做的事情是遍历符号表并查看存储是否指向完全相同的内存位置。

    有点像这样:

    use Scalar::Util qw<refaddr>;
    my %seen;
    
    sub _descend_symtable {
        $calls++;
        my ( $cand, $stash_name ) = @_;
        my $stash = do { no strict 'refs'; \%{ $stash_name }; };
        return if $seen{ refaddr( $stash ) }++;
        return $stash_name if $cand == $stash;
    
        my $result;
        foreach my $s ( grep { m/::$/ } keys %$stash ) {
            $result = _descend_symtable( $cand, "$stash_name$s" ) 
                and return $result;
        }
        return;
    }
    
    sub find_in_symtable { 
        my $needle = shift;
        %seen      = ();
        return _descend_symtable( $needle, 'main::' );
    }
    

    表现并不糟糕

    【讨论】:

    • 这是一个很好的开始,我想我可以完成 95% 的工作。对于空包(foo(\%empty::package)foo({})),此测试将是模棱两可的,并且可能被误报 foo( { 'abc::' =&gt; *main::abc:: }) 或误报($emptypkg::{"def"} = "xyz"; foo(\%emptypkg::))欺骗。还有什么更万无一失的吗?
    猜你喜欢
    • 2019-10-31
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 2013-05-01
    • 2017-02-19
    • 2018-12-02
    • 2018-06-10
    • 2012-07-24
    相关资源
    最近更新 更多