2009-11-04 13 views
10

配列参照の文字列化されたバージョン(ARRAY(0x8152c28))を実際の配列参照に変換する方法はありますか?例どのように文字列化されたバージョンの配列参照をPerlの実際の配列参照に変換できますか?

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

については

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

「Dumper($ b)」を超えてどのような魔法が必要なのか分かりません。明らかにあなたは '警告する 'ことを望む。あなたはいつ警告しますか? '$ a'を文字列化することができるほとんどの場所で、私は' $ a'をダンプします。 – Axeman

+0

文字列をオーバーロードして番号を返す方が簡単です。 – MkV

+0

'eval $ thing'がほしいと思うように聞こえます。もちろん、これは文字列化された方法に依存します。 Data :: DumperまたはData :: Dumpを使用した場合、単に 'eval'ingするとオブジェクトが再作成されます。 – simbabque

答えて

17

はい、(インライン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です。

+0

これを行うためのモジュールが2つあります.1つはXSで、もう1つは上記と似ています。しかし、それらを使用しないでください。 :) – ysth

+2

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

+0

リファレンスカウントを増やしたい場合は、AVを取得してもまだ存在するものと仮定して*大丈夫かもしれません。 – Axeman

6

をもたらすであろう最初の質問は:あなたは本当にこれをしたいですか?

その文字列はどこから来ていますか?

あなたのPerlプログラムの外部から来ている場合は、ポインタ値(16進数)は無意味になりますし、それを行う方法はありません。

あなたのプログラムの中から来ている場合は、まずそれを文字列にする必要はありません。

+1

さらに、状況によっては、配列がGCedになります。その時点で、文字列化されたバージョンの参照を有効な配列に変換できなくなります。 – outis

+0

私は実際にこれをやりたかったとします。また、データはまだ参照されるだろう、私はこれを反映するために質問のコードを更新した。ガベージコレクションは問題ではありません。 – Tim

+1

@Tim - あなたの人生をより楽にするために、私はあなたがこれをしたいと思うことを知りたいと思っています。これを行うには正当な理由があるかもしれませんが、そうであれば、これまで提供されてきた優れた答えで自分自身をノックアウトしてください。しかし、私は何とかあなたがやろうとしていることを達成するためのより良い方法があるかもしれないと思っています。 –

4

文字列化されたバージョンには配列オブジェクトのメモリアドレスが含まれています。したがって、それを復元できます。このコードは、とにかく(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

私はなぜこれをしたいのか分かりませんが、本当に必要な場合は、トリックを使ってメモリを調べる答えは無視してください。彼らはあなたに問題を引き起こすだけです。

なぜこれをやりたいですか?おそらくより良いデザインがあります。どこからその文字列化された参照を取得していますか?

何らかの理由でそれを行う必要があるとします。

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を試してみて、それがその詳細のすべてを処理させることができます。

これより長い例がIntermediate Perlにあります。

+0

これは私が実際にしたものです。誰かがTie :: RefHashの使用を提案しましたが、これは私が望むものを達成しました。私は他のところで、質問は私が実際にやらなければならないものよりも私にとって好奇心であるとコメントしました。懸念してくれてありがとう。 – Tim

+0

Tie :: RefHashを使用するのではなく、レジスタ、id、およびid_2obj関数でHash :: Util :: FieldHash(Hash :: Util :: FieldHash :: Compat before 5.10)を使用してください。さらに、キーとして、IDはスレッドに残っており、自動的にガベージコレクションされます。 – MkV

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); 
+0

ありがとう! ./findref.pl 8行目では、16進数> 0xffffffffのようなメッセージは得られませんが、これはうまく動作します。私はなぜPerlの組み込みの 'hex 'が参照のアドレスと同じくらい高い値を扱えないのだろうかと思います。 – Mohith

0

は、セグメンテーションフォールトを検出するためのサポートを追加することによって、この便利な、私は拡張していますtobyinkの答えを見つけました。私が発見した2つのアプローチがあります。逆参照の前に、$SIG{SEGV}$SIG{BUS}をローカルに置き換える最初の方法があります。第2の方法はmasks the child signalで、分岐した子が逆参照できたかどうかを確認します。最初の方法は2番目の方法よりも大幅に高速です。

誰でもこの回答を改善することができます。

最初のアプローチ

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の戻り値がチェックする必要があるかはわかりません。

関連する問題