Как преобразовать стробированную версию ссылки на массив в ссылку на массив в Perl?

Есть ли способ заставить Perl преобразовать строчную версию, например (ARRAY (0x8152c28)) ссылки массива на реальную ссылку на массив?

Например

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

даст

$VAR1 = [
      1,
      2,
      3
    ];

Ответ 1

Да, вы можете это сделать (даже без Inline C). Пример:

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

но не делайте этого. Если ваш фактический объект освобожден или повторно использован, вы можете очень хорошо в конечном итоге получить segfaults.

Что бы вы ни пытались достичь, безусловно, лучший способ. Комментарий к другому ответу показывает, что строение связано с использованием ссылки в качестве хэш-ключа. Как ответил на это, лучший способ сделать это - проверенный Tie::RefHash.

Ответ 2

Первый вопрос: вы действительно хотите это сделать?

Откуда взялась эта строка?

Если это происходит извне вашей программы Perl, значение указателя (шестнадцатеричные цифры) будет бессмысленным, и нет способа сделать это.

Если это происходит изнутри вашей программы, тогда нет необходимости в том, чтобы свести ее в первую очередь.

Ответ 3

Строковая версия содержит адрес памяти объекта массива, поэтому да, вы можете его восстановить. В любом случае, этот код работает для меня (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

Ответ 4

Да, возможно: используйте 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);

Ответ 5

Я не уверен, почему вы хотите это сделать, но если вам это действительно нужно, игнорируйте ответы, которые используют трюки, чтобы заглянуть в память. Они вызовут только проблемы.

Почему вы хотите это сделать? Там, вероятно, лучший дизайн. Откуда вы получаете эту строчную ссылку.

Скажем, вам нужно сделать это по любой причине. Во-первых, создайте реестр объектов, в которых хэш-ключ является строковой формой, а значение является ослабленной ссылкой:

 use Scalar::Util qw(weaken);

 my $array = [ ... ];

 $registry{ $array } = $array;

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

Теперь, когда у вас есть строчная форма, вы просто просматриваете ее в хэше, проверяя, что она все еще является ссылкой:

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

Вы также можете попробовать Tie:: RefHash и позволить ему обрабатывать все детали этого.

Более длинный пример этого в Промежуточный Perl.

Ответ 6

В случае, если кто-то найдет это полезным, я продолжаю отвечать на запросы, добавив поддержку для обнаружения ошибок сегментации. Я обнаружил два подхода. Первый способ локально заменяет $SIG{SEGV} и $SIG{BUS} перед разыменованием. Второй способ маскирует дочерний сигнал и проверяет, может ли разветвленный ребенок разыменоваться. Первый способ значительно быстрее второго.

Кто-нибудь может улучшить этот ответ.

Первый подход

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

Я не уверен, что могут возникнуть какие-либо другие сигналы при попытке получить доступ к незаконной памяти.

Второй подход

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

Я не уверен, что нужно проверить возвращаемое значение waitpid.