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

Hi all

I've got a bit of trouble with perl's tie...

Run the following code width different options ( STORE, CLEAR,FETCH, ...) :
#!/opt/perl5/bin/perl -w use strict; package toy::hash; use strict; sub TIEHASH { my $class = shift; return bless { values => { @_ } }, $class; } sub STORE { my ( $self, $key, $value ) = @_; &::is_H_tied(); $self->{values}{$key} = $value; } sub FETCH { my ( $self, $key ) = @_; &::is_H_tied(); $self->{values}{$key}; } sub KEYS { my ( $self ) = @_; &::is_H_tied(); keys %{ $self->{values} }; } sub FIRSTKEY { my ( $self ) = @_; &::is_H_tied(); each %{ $self->{values} }; } sub NEXTKEY { my ( $self ) = @_; &::is_H_tied(); each %{ $self->{values} }; } sub CLEAR { my ( $self ) = @_; &::is_H_tied(); %{ $self->{values} } = (); } sub EXISTS { my ( $self, $key ) = @_; &::is_H_tied(); exists $self->{values}{$key}; } package main; our $H; sub is_H_tied { my $m = ( caller( 1 ) )[3]; print STDERR "method=$m, tied(\%\$H)=", tied( %$H ), "\n"; } my @k = ( 1..30 ); tie( my %h, 'toy::hash', map { $_, $_ * $_ } @k ); $H = \%h; die "\n" unless( @ARGV ); for( $ARGV[0] ) { /^CLEAR$/o && do { %h = (); tied( %h )->CLEAR(); last; }; /^STORE$/o && do { $h{pi} = 3.1415926; tied( %h )->STORE( qw( pi 3.1415926 ) ); last; }; /^FETCH$/o && do { my $x = $h{pi}; tied( %h )->FETCH( 'pi' ); last; }; /^DELETE$/o && do { delete $h{pi}; tied( %h )->DELETE( 'pi' ); last; }; /^KEYS$/o && do { keys %h; tied( %h )->KEYS(); last; }; /^EXISTS$/o && do { exists $h{pi}; tied( %h )->EXISTS( 'pi' ); last; }; }
In this script, I tie %h to toy::hash, and keep a reference to %h in $H. Then, while working with the object which holds the tie, I check whether %$H is tied or not.

It seems that %h = () and tied( %h )->CLEAR(); are not equivalent, for when the first is invoked, %$H is not tied to toy::hash anymore, while tied( %h )->CLEAR() does not break the tie of %$H.

I've ran another script with a TIEARRAY, and some methods break the tie, while some others don't.

So my question is :
Is this a bug or an undocumented feature of perl's tie ?

philou

Edit: s/pre/code/. larsen

update (broquaint): added <readmore> tag

Replies are listed 'Best First'.
Re: Where's my tie ?
by pg (Canon) on Dec 01, 2002 at 16:54 UTC
    First thing I noticed is that:
    • you didn't specify @ISA = (Tie::Hash); (or StdHash, ExtraHash) in your toy::hash package.
    • You are not supposed to call those CLEAR, DELETE etc directly, you are supposed to just use toy::hash as a usual hash.
    A quick demo:
    package myhash; require Tie::Hash; @ISA = (Tie::StdHash); sub DELETE { print "in my own delete"; #it should do something more meaningful, +this is just a demo } 1; myhash.pl: (a testbed) use myhash; tie %a, 'myhash'; delete $a{"a"};
      I don't need to use another existing class as a base for my toy::hash package, because I have defined all required methods (I think).
      The other thing is that I know that I should not call tie methods myself but let perl do the job for me.
      The issue is that I get a strange behaviour of the %$H's tie when I do so.

      philou
Re: Where's my tie ?
by demerphq (Chancellor) on Dec 02, 2002 at 10:17 UTC
    Ok, first off a little comment about posting ettiquette. Please use <code> tags in the future. You apparently didnt notice that your code does not render the way it is typed but the code is unrunnable from a copy/paste.

    Second, please in the future provide code THAT ILLUSTRATES the bug. Don't write a command line parser that we then have to figure out how to use before we can see the problem. I deleted just about the entire main because for me it was useless. Also, i think that if you had done a little more rigourous searching you would have seen (assuming you are on 5.6.1) that you have found a bug, but it isnt the one you think. (see the code at the bottom that generated this)

    Ref $H: HASH(0x1c19d50) Before STORE: %$H - %$H - Toy::Hash=HASH(0x1c199a4) %h -Toy::Hash=H +ASH(0x1c199a4) in Toy::Hash::STORE, tied(%$H) is Toy::Hash=HASH(0x1c199a4) Ref:(HASH( +0x1c19d50)) in Toy::Hash::STORE, tied(%h) is Toy::Hash=HASH(0x1c199a4) After STORE: %$H - Toy::Hash=HASH(0x1c199a4) %h -Toy::Hash=HASH(0x +1c199a4) Ref $H: HASH(0x1c19d50) Before CLEAR: %$H - Toy::Hash=HASH(0x1c199a4) %h -Toy::Hash=HASH(0x +1c199a4) in Toy::Hash::CLEAR, tied(%$H) is '' Ref:(HASH(0x1c19d50)) in Toy::Hash::CLEAR, tied(%h) is '' After CLEAR: %$H - Toy::Hash=HASH(0x1c199a4) %h -Toy::Hash=HASH(0x +1c199a4) Ref $H: HASH(0x1c19d50)
    So clearly the %$H is indeed tied after the CLEAR. However it appears that for some reason it and %h itself ar not tied _during_ the scope of the CLEAR itself. Presumably this shouldn't be a problem and is probably related to the implementation of the TIE internally (there are a number of known bugs in the TIE implementation, this would be just one more. Nevertheless It Will get reported.)

    BTW: here is a (more) minimal piece of code that demonstrates your bug, this is the kind of thing people would prefer to see:

    package Toy::Hash; use strict; use warnings; sub TIEHASH { my $class = shift; return bless { values => { @_ } }, $class; } sub STORE { my ( $self, $key, $value ) = @_; ::is_H_tied(); $self->{values}{$key} = $value; } sub CLEAR { my ( $self ) = @_; ::is_H_tied(); %{ $self->{values} } = (); } package main; use strict; our $H; my @k = ( 1..30 ); tie( my %h, 'Toy::Hash', map { $_, $_ * $_ } @k ); $H = \%h; sub is_H_tied { my $m = ( caller( 1 ) )[3]; print "in $m, tied(\%\$H) is ". (tied( %{$::H} ) || "''"). " Ref:($H +)\n"; print "in $m, tied(\%h) is ". (tied( %h ) || "''"). "\n"; } print "Ref \$H: $H\n\n"; print "Before STORE: %\$H - %\$H - ",tied(%$H) . "\t\%h -" . tied(%h). + "\n"; $H->{foo}="bar"; print "After STORE: %\$H - ",tied(%$H) . "\t\%h -" . tied(%h). "\n\n" +; print "Ref \$H: $H\n\n"; print "Before CLEAR: %\$H - ",tied(%$H) . "\t\%h -" . tied(%h). "\n"; %$H=(); print "After CLEAR: %\$H - ",tied(%$H) . "\t\%h -" . tied(%h). "\n\n" +; print "Ref \$H: $H\n";
    HTH

    UPDATE:This matter has been posted to P5P.

    --- demerphq
    my friends call me, usually because I'm late....

      Thanks for your advice.
      The fact that %$H is not tied in CLEAR's scope is actually a problem for me, but since it's the only hash method which breaks %$H's tie, I will have to go around.

      NB: If you write the same sort of test code for a TIEARRAY, you get the same side effects for some methods like FETCHSIZE.

      philou
Re: Where's my tie ?
by demerphq (Chancellor) on Dec 02, 2002 at 13:46 UTC
    Ok. Ive just recieved an explaination of this matter from the kind folks at p5p.

    Turns out that the behaviour you have stumbled upon is an undocumented feature to prevent horrible horrible things from happening with self tied objects. Basically before any tie related operation occurs the thing it affects has its Magic bits temporarily suspended to prevent infinite recursion.

    The reason why it doesnt _seem_ to happen on a FETCH/STORE but does on CLEAR is because FETCH/STORE affects an element (and that element does indeed have its magic bit cleared) wheras CLEAR affects the whole object. This also explains the behaviour of the other Tie types. Where the autocalled method affects the overall tie it will seem as though the object isn't tied for the duration of that method.

    BTW, why do you need this behaviour? I'm interested in the core problem you are facing. For instance why isn't the access to $self sufficient for your needs?

    --- demerphq
    my friends call me, usually because I'm late....