How do I get address of scalars?

Discussion in 'Perl Misc' started by Dez, Jan 13, 2013.

  1. Dez

    Dez Guest

    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, Jan 13, 2013
    #1
    1. Advertising

  2. Dez

    Dez Guest

    On Mon, 14 Jan 2013 01:13:45 +0000, Ben Morrow wrote:

    > Quoth Dez <>:
    >> 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.
    Dez, Jan 14, 2013
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ray Gardener

    Inheriting scalars

    Ray Gardener, Jun 9, 2004, in forum: C++
    Replies:
    7
    Views:
    419
    Ray Gardener
    Jun 10, 2004
  2. Replies:
    8
    Views:
    314
    Paul Boddie
    Mar 1, 2005
  3. ago
    Replies:
    3
    Views:
    322
  4. Replies:
    2
    Views:
    9,019
    Steve Holden
    Feb 28, 2008
  5. Jeff Thies

    scalars and namespace

    Jeff Thies, Jun 27, 2003, in forum: Perl Misc
    Replies:
    4
    Views:
    82
    Kent Paul Dolan
    Jun 28, 2003
Loading...

Share This Page