Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Method call to tied hash leads to file read error

by Cirollo (Friar)
on May 28, 2003 at 16:15 UTC ( #261350=perlquestion: print w/replies, xml ) Need Help??
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 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.


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.


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://261350]
Approved by Mr_Person
[ovedpo15]: another question about regex. if I have a string like this: "a/b/,c/d/e,5" I would like to remove the string after the last comma so the output will be "a/b/,c/d/e" how to do so?
[Corion]: ovedpo15: You could split on comma, and then join everything together, except the last part. Or you could match and make sure that the last part has no comma, like qr!,[^,]+$!, or you could use rindex.
[ovedpo15]: But I don't know the length of the string. will it still work?
[ovedpo15]: The only thing I know is that there is a comma at the end and a value after that
[hippo]: Try it and see. Your computer won't explode.
[Corion]: length will tell you the length of a string.
[Corion]: But if you are parsing CSV, again, let me point to Text::CSV_XS, which you should use instead
[hippo]: +1 for Text::CSV_XS
[ovedpo15]: Im not allowed to use additional modules, so I can't use it. looking for a simple regex to do this task though..

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (9)
As of 2018-05-27 10:36 GMT
Find Nodes?
    Voting Booth?