2011-03-01 7 views
11

¿Hay alguna forma de saber si una referencia hash se refiere a una tabla de símbolos?¿Cómo se distingue una tabla de símbolos de una variable hash regular?

Es decir, ¿cómo podría la función

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

saber si había sido invocada como

foo(\%main::) 

en lugar de

foo(\%main) 

?

La aplicación es una función que a veces tie tiene variables hash, pero me gustaría evitar intentar atar una tabla de símbolos.

Respuesta

7

Parece que esto se puede hacer desde la API C usando HvNAME. Lo siguiente es de perlapi:

HvNAME

Devuelve el nombre del paquete de un escondite, o NULL si escondite no es un alijo. Ver SvSTASH, CvSTASH.

  1. char * HvNAME (HV * escondite)
+2

Estaba comenzando a deducir esto de ejecutar 'perl -MDevel :: Peek -e 'Dump (\% a: :), Dump (\% a)''. Gracias por el enlace, así que puedo hacer menos culto a la carga. Sin XS todavía puede hacer algo como '$ is_a_symtable = do {$ sv = B :: svref_2object ($ hashref); ref ($ sv) eq 'B :: HV' && $ sv-> NAME};' – mob

3

Puede buscar las claves que terminen en '::', lo que indicaría que tiene otros paquetes, o todos los valores son referencias de símbolos.

  • Por supuesto, incluso aquí sería difícil distinguir entre un hash y simplemente el almacenamiento de símbolos (por la razón que sea). Estaba hurgando con B::svref_2object, pero incluso los símbolos de un alijo almacenado en un hash regular devolverían algo para $sym->can('STASH').

Creo que lo que podría hacer es descender a través de la tabla de símbolos y ver si un escondite apunta exactamente a la misma ubicación de memoria.

Algo así como esto:

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::'); 
} 

El rendimiento no fue terribles.

+0

Este es un gran comienzo y que me conseguir el 95% de la manera allí, creo. Esta prueba sería ambigua para los paquetes vacíos ('foo (\% empty :: package)' vs. 'foo ({})') y podría ser falsificada para los falsos positivos 'foo ({'abc ::' => * main :: abc ::}) 'o falsos negativos (' $ emptypkg :: {"def"} = "xyz"; foo (\% emptypkg: :) '). ¿Algo más infalible? – mob

Cuestiones relacionadas