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

I am trying to write a generic 'set' method. A simplified fragment:
my %drink; # other attribute hashes sub set { my ($key, $attr, $value) = @_; my $hashref; eval "\$hashref = \\\%$attr"; if ( !defined $hashref ) { carp 'Invalid attribute name'; } else { $hashref->{$key} = $value; } }
The call to this subroutine:
set('larry', 'drink', 'Old Speckled Hen');
This does not work, i.e. it does not actually alter the 'real' hash (%drink). I have discover two fixes, but I cannot explain why either works 1. If I stringify the actual hash (%drink). After the assignment, if I add:
print "$drink{$key}\n";
it works. It seems to 'commit' $hashref. Using Data::Dumper or Devel::Peek::Dump also fixes it. 2. If I make the hash an 'our' variable it works. I'm setting warnings and use strict. Tried on 5.8.6 on Fedora and 5.8.7 on Windows. What is going on?

Replies are listed 'Best First'.
Re: eval a reference to a my hash
by theorbtwo (Prior) on Apr 05, 2006 at 12:13 UTC

    I don't understand why this doesn't work, but I do have a suggestion about how to fix it. Instead of using seperate hashes with different names, have a hash of hashrefs. Then you don't need an eval STRING or symbolic references to make it work.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      Nice solution, I wanted to get rid of the eval anyway. Thanks!
      I would still love to know why the original doesn't work.

        By the time the eval STRING code runs, lexicals don't really have names anymore, so Perl can't look them up by name.

        When you say the original doesn't work, what makes you think it isn't?

        It seems your simplified fragment is not demonstrating the problem, because:

        #!/usr/bin/perl -w + # Strict use strict; use warnings; + # Libraries use Carp; use Data::Dumper; + + my %drink; # other attribute hashes + set('larry', 'drink', 'Old Speckled Hen'); print $drink{'larry'}, "\n"; # print "Dump(\%drink) => [%s]\n", Dumper(\%drink); + + sub set { my ($key, $attr, $value) = @_; + my $hashref; eval "\$hashref = \\\%$attr"; + if ( !defined $hashref ) { carp 'Invalid attribute name'; } else { $hashref->{$key} = $value; } }
        is working for me:
        % prog541345.pl Old Speckled Hen
        If that isn't what you're getting, please compare the above code fragment (based on what you provided in your post) to your actual code, and see if that helps locate the problem.

        s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/