2011-03-01 28 views
11

有没有什么办法可以判断一个散列引用是否指向一个符号表?如何区分常规散列变量中的符号表?

也就是说,如何可以在功能

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

知道它是否已经被援引作为

foo(\%main::) 

而不是

foo(\%main) 

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

回答

7

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

HvNAME

如果藏匿不是藏匿返回藏匿, 或NULL的包名。见 SvSTASH,CvSTASH。

  1. 的char * HvNAME(HV *藏匿)
+2

我刚刚开始从运行'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};' – mob 2011-03-02 02:30:28

3

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

  • 当然,即使在这里,也很难从哈希中存储符号(无论出于何种原因)。我在与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::'); 
} 

的表现并不可怕

+0

这是一个很好的开始,并会得到我的方式出现95%,我想。这个测试对于空包('foo(\%empty :: package)'vs'foo({})')是不明确的,并且可能因为误报而被欺骗foo({'abc ::'=> * main :: abc ::})或者否定否定('$ emptypkg :: {“def”} =“xyz”; foo(\%emptypkg::)')。更安全吗? – mob 2011-03-01 22:29:00