Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Perl Misc (http://www.velocityreviews.com/forums/f67-perl-misc.html)
-   -   How do I get address of scalars? (http://www.velocityreviews.com/forums/t956471-how-do-i-get-address-of-scalars.html)

Dez 01-13-2013 04:27 AM

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


Dez 01-14-2013 02:35 AM

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.