Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.

Readonly error on $_

by jeroenes (Priest)
on Jan 05, 2001 at 15:56 UTC ( [id://50035] : perlquestion . print w/replies, xml ) Need Help??

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

I encountered something that puzzles me. I have a workaround, but I'd like to know why it happens. If you would be so kind to share your wisdom with me, or at least point me to some docs, I'd be very grateful. It's my very first OOP project in perl, so please be patient with me.

In short, a function that does a SWITCH:{$_ = .... get's an error 'Modification of a read-only value attempted' when when two function calls back a $_ was passed as the first parameter. It's getting lengthy here, so....

Let's start with the last function. After two hard-to-debug errors, I decided to write a module that checks whether a method was called with a $self->. This module is exports the function selfcontrol and contains the following code:

package Selfcontrol; use strict; use Exporter; use vars qw( @EXPORT @ISA @VERSION); @VERSION = 0.01; @ISA = qw( Exporter ); @EXPORT = qw( &selfcontrol); use Carp; sub selfcontrol{ my $self = shift; my $ref = ref( $self); SWITCH: { $_ = $ref; last unless $_; last if /REF/; last if /SCALAR/; last if /ARRAY/; last if /HASH/; last if /CODE/; last if /GLOB/; return $self; } confess 'You forget the $self->, you idiot!'. "\n\tInstead you gave me $self that refs to '$ref'"; }
Normally, this works. I call this function in the first line of a method this way:
my $self = $bugfree ? shift : selfcontrol( shift );
because I fear the selfcontrol will slow things up when it's called too often, and I have a feeling that using modules to find out the caller would not greatly improve on speed. $bugfree is a global scalar declared with use vars.

Well, somewhere in my actual class (well, at line 300 of over 1100) I use a for loop:

for ('Operator', 'SQLStatement', 'PerlVariable', 'CompModifier +') { $self->findTokenPos( $_, $i, $blocklast ); $numberOthers += scalar( @_ ); }
The findTokenPos first does the selfcontrol magic, and it gets beaten like hell:
# # Modification of a read-only value attempted, chunk 1. File ''; Line 15 # Selfcontrol::selfcontrol('embedSQL=HASH(0x2b31864)') called File ''; Line 876 # embedSQL::findTokenPos('embedSQL=HASH(0x2b31864)', 'Operator', 2 +, 3) called File ''; Line 292 # embedSQL::synonymsSELECT('embedSQL=HASH(0x2b31864)') called File ''; Line 202 # embedSQL::parse('embedSQL=HASH(0x2b31864)') called File ''; Line 10
This is my workaround:
for my $str ('Operator', 'SQLStatement', 'PerlVariable', 'Comp +Modifier') { $self->findTokenPos( $str, $i, $blocklast ); $numberOthers += scalar( @_ ); }
And hey, no errors any more! Could anyone please telling me why the first for loop doesn't work? Thanks a lot!

I was dreaming of guitarnotes that would irritate an executive kind of guy (FZ)

Replies are listed 'Best First'.
Re: Readonly error on $_
by merlyn (Sage) on Jan 05, 2001 at 20:37 UTC
    Not commenting on the larger task of what you're trying to do, the smaller task you're trying to do is clobbering $_. I'd rewrite that as:
    sub selfcontrol { my $self = shift; defined $_ and length $_ and not (/REF/ or /SCALAR/ or /ARRAY/ or /HASH/ or /CODE/ or /GLOB/) a +nd return $_ for ref $self; carp "diem"; }
    But it's probably simpler to use
    sub selfcontrol { my $self = shift; return $self if eval {$self->can("can")}; # Only objects can dance! carp "diem"; }

    -- Randal L. Schwartz, Perl hacker

      Thanks, merlyn!

      Nice rewrites. Very nice use of for. I think I should that construction a much more many times in my code!!

      The second one, with the can, requires some more study from my side. Will dive into those perldocs! Or maybe you mean to use a real method instead of can? I wouldn't think that will work, as I plan to use this for more classes. Update: immediately found can in perlobj. It's all clear now.

      Thanks again,

      I was dreaming of guitarnotes that would irritate an executive kind of guy (FZ)

      Note that your second example doesn't test for an object but for an object or a package name and Perl considers most strings that don't contain control characters to be package names. This is easy to fix:

      sub selfcontrol { my $self = shift; return $self if ref($self) && eval {$self->can("can")}; carp "diem"; }

              - tye (but my friends call me "Tye")