dash2 has asked for the wisdom of the Perl Monks concerning the following question:

OK, I am utterly stumped and stupefied.

Here's some code that attempts to take a string, convert it to a reference to a particular part of a hash of hashes, and give that part another value. I've simplified it down to a test case.

#!/usr/bin/perl $CONFIGS={}; &change_struct(string_to_struct("CONFIGS-hosting_plans-c-title"),"ctit +le"); print $CONFIGS->{hosting_plans}{c}{title}; exit; sub change_struct { my $struct = shift; $$struct = shift; } sub string_to_struct { my $string = shift; my ($var, $keys) = split /-/, $string, 2; $keys = "-$keys"; # safety check would normally be here - removed for simplicity no strict refs; my $ref = $$var; use strict refs; return deref($ref, $keys); } sub deref { return \$_[0] unless $_[1] =~ s/^-([\w\._]+)//; deref ($_[0]->{$1}, $_[1]); }

Under Perl 5.005_03, this gives nothing. Under Perl 5.6, it works fine ( "ctitle" is printed ).

Weirder still -

under the 5.005_03 debugger,
W $CONFIGS->{hosting_plans}{c}{title}
results in

DB<2> c Watchpoint 0: $CONFIGS->{hosting_plans}{c}{title} changed: old value: undef new value: '($@, $!, $^E, $,, $/, $\, $^W) = @saved;package main; + $CONFIGS->{hosting_plans}{c}{title}; ;'

just before the "print". Under the 5.6 debugger, no such problem: the value is changed to ctitle.

What is this? I am lost and bemused, and this has been a hell of a nasty bug so far.

dave hj~

Replies are listed 'Best First'.
(tye)Re: weird reference bug with perl 5.005_03
by tye (Sage) on Apr 12, 2001 at 22:52 UTC

    Looks like a bug in Perl but I can't say I blame Perl much for this one!

    What you do above is take a reference to a non-existant hash value and then later try to use that as an anonymous hash in order to take another reference to a non-existant hash value. Then you assign to the last reference and expect all of the previous non-existant hash values to get autovivified into anonymous hashes!! I'm amazed it even works as well as it does!

    This line: deref ($_[0]->{$1}, $_[1]); calls deref with a first argument of undef. Note that that line doesn't assign to $_[0]->{$1} so it doesn't autovivify anything at that point. Later you use that undef in that same line to get another undef. You are lucky that at this point Perl manages to autovivify the previous undef into an anonymous hash. It appears that in Perl 5.005 and 5.004, this isn't done perfectly so strange things can happen after this, somewhat at random (based on my testing).

    You can fix the problem by removing the need to autovivify:

    sub deref { return \$_[0] unless $_[1] =~ s/^-([\w\._]+)//; $_[0]->{$1} ||= {}; # Vivify an anonymous hash if needed. deref ($_[0]->{$1}, $_[1]); }

            - tye (but my friends call me "Tye")
Re: weird reference bug with perl 5.005_03
by Rhandom (Curate) on Apr 12, 2001 at 22:59 UTC
    I don't know how much liberty you have to change the code, but this modification seems like it would make life a thousand times simpler and is certainly less obfuscated. Instead of "ctitle" you could pass a reference to any structure and it will correctly reference it. It is not quite as strict in parsing the key names but that would be simple to add.
    #!/usr/bin/perl $CONFIGS={}; string_to_struct("CONFIGS-hosting_plans-c-title","ctitle"); print $CONFIGS->{hosting_plans}{c}{title}; exit; sub string_to_struct { my $string = shift; my $val = shift; my ($var, $keys) = split /-/, $string, 2; # safety check would normally be here - removed for simplicity no strict refs; my $ref = $$var; use strict refs; my $pointer = $ref; my @a = split(/-/,$keys); foreach ( 0..$#a ){ $pointer->{$a[$_]} = ($_ == $#a) ? $val : {}; $pointer = $pointer->{$a[$_]}; } $ref; }