http://www.perlmonks.org?node_id=261350

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

I wrote a module using cached hashes to access methods, as in the node A cacheing tied hash base class. The point is that you can access data using things like $object->{method}. After writing a moderately sided module around this, I've come across a strange bug. Depending on the way I call the method, sometimes I get a "Modification of a read-only value" error at the end of a file read.

Here's some code that reproduces the problem. Run it with the path to a file as an argument. The first way of calling the method will read and print out every line in the file, but the second way causes a fatal error, which occurs on the last line of the file (I think).

#!/bin/perl -w package My::Module; use vars qw(@ISA); use Carp; use Tie::Hash; @ISA = qw(Tie::StdHash); sub TIEHASH { my($class) = shift; my($self) = { }; bless $self, $class; } sub FETCH { my($self, $key) = @_; unless (exists $self->{$key}) { if (my $meth = $self->can($key)) { $self->{$key} = $meth->($self); } else { warn "No method for '$key'"; $self->{$key} = undef; } } return $self->{$key}; } sub new { my($class) = shift; my(%hash); tie %hash, $class; $hash{filename} = shift; return \%hash; } sub my_method { my $self = shift; open(IN, $self->{filename}) or croak "Couldn't open file $self->{f +ilename}: $!"; while (<IN>) { print; } close IN; } 1; package main; # THIS WORKS: my $obj1 = new My::Module($ARGV[0]); my $method = 'my_method'; print $obj1->{$method}; # THIS DOESNT WORK -- error message: # Modification of a read-only value attempted at /home/apirkle/perl/bu +g.pm line 41. my $obj2 = new My::Module($ARGV[0]); print $obj2->{$_} for (qw|my_method|); exit;
Any thoughts on why the second way of calling the method (in a for loop) doesn't work?

Replies are listed 'Best First'.
Re: Method call to tied hash leads to file read error
by blokhead (Monsignor) on May 28, 2003 at 16:43 UTC
    You've just experienced the joy of $_. The problem is that while (<FH>) doesn't local'ize $_. Consider this:

    You write:

    print $obj2->{$_} for (qw|my_method|);
    So now $_ is set to a read-only value. Then eventually from within that call we get to this line:
    while (<IN>) {
    ... which is syntactic sugar for:
    while (defined($_ = <IN>)) {
    You see that this line is assigning to $_ (still containing the read only 'my_method'). while didn't local'ize $_!! This is a common trap, and I know someone has written an extensive writeup about this particular trap among many CPAN modules.

    In short, put local $_; right above the while (<IN>) line, and things should work fine.

    blokhead

Re: Method call to tied hash leads to file read error
by broquaint (Abbot) on May 28, 2003 at 16:38 UTC
    It's because in the second instance you're looping over a constant value in the for and the trying to assign that in my_method e.g
    sub readstuff { return while <> } print readstuff for 'a constant value'; __output__ Modification of a read-only value attempted at - line 1.
    Where the while really compiles as return while defined($_ = <>). As has been mentioned before in IlyaM's while(<>) { ... } considered harmful, be careful when implicitly using $_ in subroutines and methods.
    HTH

    _________
    broquaint