2009-11-04 15 views
10

¿Hay alguna manera de hacer que Perl convierta la versión codificada, por ejemplo, ARRAY (0x8152c28), de una referencia de matriz a la referencia de matriz real?¿Cómo puedo convertir la versión en cadena de la referencia de matriz a la referencia de matriz real en Perl?

Por ejemplo

perl -e 'use Data::Dumper; $a = [1,2,3];$b = $a; $a = $a.""; warn Dumper (Then some magic happens);' 

produciría

$VAR1 = [ 
     1, 
     2, 
     3 
    ]; 
+0

No veo cómo eso requiere magia más allá de 'Dumper ($ b)'. Obviamente quieres 'advertir'. ¿Cuándo quieres 'advertir'? En la mayoría de los lugares donde puedo escribir '$ a', también tengo' $ a' para volcar. – Axeman

+0

Sería más fácil si sobrecargó la cadena para devolver el número. – MkV

+0

Parece que solo quieres 'eval $ thing'. Por supuesto, esto depende de la forma en que fue codificado. Si utilizó Data :: Dumper o Data :: Dump, simplemente 'eval'ing recreará sus objetos. – simbabque

Respuesta

17

Sí, puede hacerlo (incluso sin Inline C). Un ejemplo:

use strict; 
use warnings; 

# make a stringified reference 
my $array_ref = [ qw/foo bar baz/ ]; 
my $stringified_ref = "$array_ref"; 

use B; # core module providing introspection facilities 
# extract the hex address 
my ($addr) = $stringified_ref =~ /.*(0x\w+)/; 
# fake up a B object of the correct class for this type of reference 
# and convert it back to a real reference 
my $real_ref = bless(\(0+hex $addr), "B::AV")->object_2svref; 

print join(",", @$real_ref), "\n"; 

pero no hagas eso. Si su objeto real es liberado o reutilizado, muy bien puede obtener segfaults.

Lo que sea que realmente estés tratando de lograr, sin duda hay una mejor manera. Un comentario a otra respuesta revela que la cadena se debe al uso de una referencia como una clave hash. Como respondieron allí, la mejor manera de hacerlo es probando la batalla Tie::RefHash.

+0

Hay un par de módulos para hacer esto por usted, uno XS y uno similar al anterior. Pero tampoco los uses. :) – ysth

+2

Neato ......... – mob

+0

Bueno, si está dispuesto a incrementar el recuento de referencias, * podría * estar bien, suponiendo que todavía exista cuando obtenga el AV. – Axeman

6

La primera pregunta es: ¿de verdad quieres hacer esto?

¿De dónde viene esa cuerda?

Si proviene de fuera de su programa Perl, el valor del puntero (los dígitos hexadecimales) no tendrá sentido, y no hay forma de hacerlo.

Si viene desde el interior de su programa, no hay necesidad de escribirlo en primer lugar.

+1

Además, en algunas circunstancias, la matriz obtendrá GCed, momento en el que la versión codificada de la referencia ya no se puede volver a convertir en una matriz válida. – outis

+0

Supongo que realmente quería hacer esto. También se seguiría haciendo referencia a los datos, actualicé el código en la pregunta para reflejar esto. Así que la recolección de basura no es un problema. – Tim

+1

@Tim - En un esfuerzo por hacer que tu vida sea más fácil, me interesaría mucho saber por qué quieres hacer esto. Puede haber una razón válida para hacer esto, y si es así, olvídate de las excelentes respuestas proporcionadas hasta ahora. Sin embargo, de alguna manera sospecho que puede haber una mejor manera de lograr lo que estás tratando de hacer, y te garantizo que será mucho más estable y fácil de mantener si encuentras la mejor manera de hacerlo. –

4

La versión en cadena contiene la dirección de memoria del objeto de la matriz, por lo que sí, puede recuperarla. Este código funciona para mí, de todos modos (Cygwin, perl 5.8):

use Inline C; 
@a = (1,2,3,8,12,17); 
$a = \@a . ""; 
print "Stringified array ref is $a\n"; 
($addr) = $a =~ /0x(\w+)/; 
$addr = hex($addr); 
$c = recover_arrayref($addr); 
@c = @$c; 
print join ":", @c; 
__END__ 
__C__ 
AV* recover_arrayref(int av_address) { return (AV*) av_address; } 

.

$ perl ref-to-av.pl 
Stringified array ref is ARRAY(0x67ead8) 
1:2:3:8:12:17 
2

No estoy seguro de por qué quieres hacer esto, pero si realmente lo necesitas, ignora las respuestas que usan los trucos para buscar en la memoria. Solo te causarán problemas.

¿Por qué quieres hacer esto? Probablemente haya un mejor diseño. De dónde sacas esa referencia codificada de.

Digamos que debe hacerlo por cualquier razón. En primer lugar, crear un registro de objetos donde la clave hash es la forma stringified, y el valor es una referencia debilitada:

use Scalar::Util qw(weaken); 

my $array = [ ... ]; 

$registry{ $array } = $array; 

weaken($registry{ $array }); # doesn't count toward ref count 

Ahora, cuando se tiene la forma stringified, sólo mirar hacia arriba en el hash, comprobando para ver que sigue siendo una referencia:

if(ref $registry{$string}) { ... } 

también puede probar Tie::RefHash y se deja manejar todos los detalles de este.

Hay un ejemplo más largo de esto en Intermediate Perl.

+0

Esto es lo que realmente hice. Aunque alguien sugirió usar Tie :: RefHash que también habría logrado lo que yo quería.Comenté en otro lugar que la pregunta era más una curiosidad para mí que algo que realmente necesitaba hacer. Gracias por la preocupación – Tim

+0

En lugar de utilizar Tie :: RefHash, use Hash :: Util :: FieldHash (Hash :: Util :: FieldHash :: Compat antes 5.10) con sus funciones de registro, id e id_2obj. Además, como claves, los identificadores sobreviven a los hilos y se recogen automáticamente como basura. – MkV

4

Sí, es posible: use Devel::FindRef.

use strict; 
use warnings; 
use Data::Dumper; 
use Devel::FindRef; 

sub ref_again { 
    my $str = @_ ? shift : $_; 
    my ($addr) = map hex, ($str =~ /\((.+?)\)/); 
    Devel::FindRef::ptr2ref $addr; 
} 

my $ref = [1, 2, 3]; 
my $str = "$ref"; 
my $ref_again = ref_again($str); 

print Dumper($ref_again); 
+0

¡Gracias! Esto funciona muy bien, aunque recibo mensajes como 'Número hexadecimal> 0xffffffff no portátil en ./findref.pl línea 8'. Me pregunto por qué el "hex" integrado de Perl no puede manejar valores tan altos como la dirección de una referencia. – Mohith

0

En caso de que alguien lo encuentre útil, extenderé la respuesta de tobyink agregando soporte para detectar fallas de segmentación. Hay dos enfoques que descubrí. La primera forma a nivel local reemplaza a $SIG{SEGV} y $SIG{BUS} antes de eliminar la referencia. La segunda forma es masks the child signal y comprueba si un niño bifurcado puede eliminar la referencia correctamente. La primera forma es significativamente más rápida que la segunda.

Cualquier persona puede mejorar esta respuesta.

Primer acercamiento

sub unstringify_ref($) { 
    use bigint qw(hex); 
    use Devel::FindRef; 

    my $str = @_ ? shift : $_; 
    if (defined $str and $str =~ /\((0x[a-fA-F0-9]+)\)$/) { 
    my $addr = (hex $1)->bstr; 

    local [email protected]; 
    return eval { 
     local $SIG{SEGV} = sub { die }; 
     local $SIG{BUS} = sub { die }; 
     return Devel::FindRef::ptr2ref $addr; 
    }; 
    } 
    return undef; 
} 

No estoy seguro de si cualesquiera otras señales pueden ocurrir en un intento de acceder a la memoria ilegal.

segundo enfoque

sub unstringify_ref($) { 
    use bigint qw(hex); 
    use Devel::FindRef; 
    use Signal::Mask; 

    my $str = @_ ? shift : $_; 
    if (defined $str and $str =~ /\((0x[a-fA-F0-9]+)\)$/) { 
    my $addr = (hex $1)->bstr; 

    local $!; 
    local $?; 
    local $Signal::Mask{CHLD} = 1; 
    if (defined(my $kid = fork)) { 
     # Child -- This might seg fault on invalid address. 
     exit(not Devel::FindRef::ptr2ref $addr) unless $kid; 
     # Parent 
     waitpid $kid, 0; 
     return Devel::FindRef::ptr2ref $addr if $? == 0; 
    } else { 
     warn 'Unable to fork: $!'; 
    } 
    } 
    return undef; 
} 

No estoy seguro de si el valor de retorno de waitpid necesita ser comprobada.

Cuestiones relacionadas