![]() |
How do I get address of scalars?
I am trying to detect circular references inside a hash. I was thinking
of doing this by tracking the address of the variables. However, the test program I have below is showing the same address for the scalars in the hash, while the references in the hash are showing as expected. Basically, I'm getting a false positive for hash member id and name. I tried the code at http://www.perlmonks.org/?node_id=406446 but it also is showing the same address, which I assume is the address of my $ref, not the address of the variable passed in. Any ideas? ---------- Output of Test Program ------- parent hash ref = 164306792 type1 = HASH blessed = key = arr ref = 163412544 type1 = ARRAY blessed = key = circ ref = 164306812 type1 = SCALAR blessed = key = circ2 ref = 164306792 type1 = HASH blessed = key = circ3 ref = 163412544 type1 = ARRAY blessed = key = code ref = 164340068 type1 = CODE blessed = key = has ref = 164340768 type1 = HASH blessed = key = id ref = 164339168 type1 = notaref blessed = key = name ref = 164339168 type1 = notaref blessed = key = obj ref = 164306732 type1 = SCALAR blessed = JSON key = regex ref = 164306712 type1 = REGEXP blessed = Regexp ---------- Test Program Below ----------- use strict; use warnings; use B; use JSON; my %tmap = qw( B::NULL SCALAR B::HV HASH B::AV ARRAY B::CV CODE B::IO IO B::GV GLOB B::REGEXP REGEXP ); sub refaddr($) { ref($_[0]) ? 0+$_[0] : undef } sub chktype { my $r = shift; return unless length(ref($r)); my $t = ref(B::svref_2object($r)); return exists $tmap{$t} ? $tmap{$t} : length(ref($$r)) ? 'REF' : 'SCALAR'; } sub walk($) { my $ref = shift; my $type1 = chktype $ref; my $type2 = ref $ref; if (!defined $type1) { $type1 = 'notaref'; } my $blessed = ($type1 ne $type2) ? $type2 : ''; # if ref is a reference, get address to which it points my $addr = $type2 ? refaddr $ref : refaddr \$ref; return "ref = $addr\ttype1 = $type1\tblessed = $blessed\n"; } my $test_hash = { name => 'Some Name', arr => ['a','b','3','5'], has => { key1 => 'val1', key2 => 'val2', key3 => 'val3' }, regex => qr/thisisaregex/x, code => sub {return 1}, id => 4, obj => JSON->new }; $test_hash->{circ} = \$test_hash->{name}; $test_hash->{circ2} = $test_hash; $test_hash->{circ3} = $test_hash->{arr}; print "\n"; print "parent hash\t" . walk $test_hash; foreach my $key (sort keys %{$test_hash}) { print "key = $key\t" . walk $test_hash->{$key}; } print "\n"; |
Re: How do I get address of scalars?
On Mon, 14 Jan 2013 01:13:45 +0000, Ben Morrow wrote:
> Quoth Dez <nawglan@gmail.com>: >> I am trying to detect circular references inside a hash. I was >> thinking of doing this by tracking the address of the variables. >> However, the test program I have below is showing the same address for >> the scalars in the hash, while the references in the hash are showing >> as expected. Basically, I'm getting a false positive for hash member id >> and name. > > Are you aware of Devel::Cycle? I think it does what you want, and it's > not quite as easy as you might think to do it correctly. However, > writing a function like this is a good way to learn about Perl's > reference model, so if you're doing it for that reason that's not a bad > idea. > >> I tried the code at http://www.perlmonks.org/?node_id=406446 but it >> also is showing the same address, which I assume is the address of my >> $ref, not the address of the variable passed in. >> >> Any ideas? >> >> ---------- Output of Test Program ------- >> >> parent hash ref = 164306792 type1 = HASH blessed = >> key = arr ref = 163412544 type1 = ARRAY blessed = >> key = circ ref = 164306812 type1 = SCALAR blessed = >> key = circ2 ref = 164306792 type1 = HASH blessed = >> key = circ3 ref = 163412544 type1 = ARRAY blessed = >> key = code ref = 164340068 type1 = CODE blessed = >> key = has ref = 164340768 type1 = HASH blessed = >> key = id ref = 164339168 type1 = notaref blessed = >> key = name ref = 164339168 type1 = notaref blessed = >> key = obj ref = 164306732 type1 = SCALAR blessed = JSON key = >> regex ref = 164306712 type1 = REGEXP blessed = Regexp >> >> ---------- Test Program Below ----------- >> >> use strict; >> use warnings; >> >> use B; >> use JSON; >> >> my %tmap = qw( >> B::NULL SCALAR B::HV HASH B::AV ARRAY B::CV CODE >> B::IO IO B::GV GLOB B::REGEXP REGEXP >> ); >> >> sub refaddr($) { >> ref($_[0]) ? 0+$_[0] : undef >> } > > use Scalar::Util qw/refaddr/; > >> sub chktype { >> my $r = shift; >> >> return unless length(ref($r)); >> >> my $t = ref(B::svref_2object($r)); >> >> return >> exists $tmap{$t} ? $tmap{$t} >> : length(ref($$r)) ? 'REF' >> : 'SCALAR'; >> } > > use Scalar::Util qw/reftype/; > >> sub walk($) { > > Don't use prototypes unless you have a very good reason. In this case > forcing scalar context on the argument is far more likely to be > confusing than helpful. > > (About the only useful use for a ($) prototype is for functions > imitating ref(), so Scalar::Util's refaddr, reftype and blessed are > ($)-prototyped.) > >> my $ref = shift; > > You've just copied $_[0] into $ref. This means that when you check > refaddr \$ref below, this will not give you the address of the variable > you passed into the function, it will give you the address of the local > $ref variable. Under normal circumstances (if the function is not > reentrant and doesn't store refs to local variables somewhere external) > this will give you the same address every time. > > What you want to do instead is take a reference to $_[0]: @_ is passed > as aliases, so this will give you a reference to the original variable > that was passed in. > > my $ref = \$_[0]; > > You will obviously then need to replace $ref with $$ref throughout. > >> my $type1 = chktype $ref; >> my $type2 = ref $ref; >> >> if (!defined $type1) { >> $type1 = 'notaref'; >> } >> >> my $blessed = ($type1 ne $type2) ? $type2 : ''; > > use Scalar::Util qw/blessed/; > > >> # if ref is a reference, get address to which it points my $addr = >> $type2 ? refaddr $ref : refaddr \$ref; > > I'm not sure if this is just a work-in-progress, but even when it's > working this will only detect circular refs of the form > > $test_hash->{circ} = \$test_hash->{name}; > > Other forms, such as > > $test_hash->{self} = \$test_hash->{self}; > > $test_hash->{ouro} = \$test_hash->{bouros}; > $test_hash->{bouros} = \$test_hash->{ouro}; > > will require checking refaddr(\$_[0]) against a hash regardless of its > type, storing it in the hash if it wasn't there, and then recursing > through its referent if $_[0] is a ref itself. > > Ben Thanks for the reply. I'm doing this as a programming exercise. 8) Basically, I'm trying to flatten a hash into an array of key/value pairs, keeping track of parent / child relationships. I want to be able to track references so that when I rebuild the object, I can restore internal references (circular) if they exist. The reason for all this is to attempt to write a dbd storage module. I know about Devel::Cycle, but as my main goal is a dbd module, I don't want to depend on it. I'm also doing all this to learn a little more about the internals of perl. I've been following perl5porters for a while now, and the traffic is very interesting. -- Dez. |
| All times are GMT. The time now is 09:10 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.