Come posso convertire la versione con stringata del riferimento di matrice in riferimento al vero array in Perl?

Esiste un modo per far sì che Perl converta la versione con stringhe, ad esempio (ARRAY (0x8152c28)) di un riferimento di matrice al riferimento effettivo dell’array?

Per esempio

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

cederebbe

 $VAR1 = [ 1, 2, 3 ]; 

Sì, puoi farlo (anche senza Inline C). Un esempio:

 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"; 

ma non farlo. Se il tuo object reale viene liberato o riutilizzato, potresti benissimo finire con l’avere segfault.

Qualunque cosa tu stia effettivamente cercando di ottenere, c’è sicuramente un modo migliore. Un commento ad un’altra risposta rivela che la stringificazione è dovuta all’utilizzo di un riferimento come chiave hash. In risposta a ciò, il modo migliore per farlo è Tie :: RefHash, ben collaudato.

La prima domanda è: vuoi davvero farlo?

Da dove viene quella stringa?

Se proviene dall’esterno del tuo programma Perl, il valore del puntatore (le cifre esadecimali) non ha senso, e non c’è modo di farlo.

Se proviene dall’interno del tuo programma, non è necessario in alcun caso doverlo stringere.

La versione con stringhe contiene l’indirizzo di memoria dell’object array, quindi sì, puoi recuperarlo. Questo codice funziona comunque per me (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 

Sì, è ansible: utilizzare 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); 

Non sono sicuro del motivo per cui vuoi farlo, ma se hai davvero bisogno, ignora le risposte che usano i trucchi per guardare nella memoria. Ti causeranno solo problemi.

Perchè vuoi fare questo? Probabilmente c’è un design migliore. Da dove prendi quel riferimento unificato?

Diciamo che devi farlo per qualsiasi motivo. Innanzitutto, creare un registro di oggetti in cui la chiave hash è il modulo con stringhe e il valore è un riferimento indebolito:

  use Scalar::Util qw(weaken); my $array = [ ... ]; $registry{ $array } = $array; weaken( $registry{ $array } ); # doesn't count toward ref count 

Ora, quando hai il form stringificato, lo cerchi nell’hash, verificando che sia ancora un riferimento:

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

Puoi anche provare Tie :: RefHash e lasciare che gestisca tutti i dettagli di questo.

C’è un esempio più lungo di questo in Perl intermedio .

Nel caso qualcuno lo trovi utile, sto estendendo la risposta tobyink aggiungendo il supporto per il rilevamento degli errori di segmentazione. Ci sono due approcci che ho scoperto. Il primo modo sostituisce localmente $SIG{SEGV} e $SIG{BUS} prima del dereferenziamento. Il secondo modo maschera il segnale figlio e controlla se un bambino biforcuto può dereferenziare correttamente. Il primo modo è significativamente più veloce del secondo.

Chiunque è il benvenuto per migliorare questa risposta.

Primo approccio

 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; } 

Non sono sicuro che possano verificarsi altri segnali nel tentativo di accedere alla memoria illegale.

Secondo approccio

 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; } 

Non sono sicuro se il valore di ritorno di waitpid debba essere controllato.