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

I'm using TIEHASH to make persistant objects. Objects are marked if they're dirty and then flushed before the script ends. The TIEHASH uses two components, %l which is the script's object cache and %f which is a GDBM_File and objects are Data::Dump'd before they're stored.

Assume %l = { 0=>{oid=>0, hi=>there}, 1=>{oid=>1, hi=>woof}} and that %o is my TIE'd object store.

This works:

print "$o{0} $o{0}->{hi}\n";
But this gives the following error:
print "$o{0}->{hi}\n";
Can't use an undefined value as a HASH reference at t2.pl line 27.

Why does pwotie's FETCH work ok with $o{0} called first, but fail when $o{0}->{hi} is called first?

Here's my source for you to try yourself:

{ # implements persistence based on dirty and tied persistence in %o package Pwo; our %o; our $keep; sub flush { for (values %o) { if($_->{dirty}) { delete $_->{dirty}; $keep->keep($_); print "k: $_->{hi} "; }}} sub Pwo { my($class,$filename)=@_; $class=ref($class) || $class; $keep=tie(%o, pwotie, $filename) || die "Pwo: Persistence failed: ", +join(', ',@dbm)," -- error:$!\n"; my $self={ 'o'=>\%o, }; bless $self,$class; # if this is a new install we need to load the base objects print "o{0}=$o{0}\n"; bootstrap() unless defined $o{0}; print "o{0}=$o{0} 0=$o{0}->{hi} 1=$o{1}->{hi}\n"; return $self; } sub bootstrap { print "bs\n"; $o{0}={hi=>there, how=>'are you', dirty=>1, oid=>0}; $o{1}={hi=>"woof",dirty=>1,oid=>1}; } } { package pwotie; use Data::Dump qw(dump); use GDBM_File; sub TIEHASH { my ($class,$filename)=@_; $Result = tie(%f, 'GDBM_File', $filename, 2, 0640); ## duno what's wr +ong with my perl -- GDBM_WRCREAT=2 for me. if (! $Result){ print "\n\nCould not open GDBM file '$filename': $!\n"; } return bless { file=>$filename, f => \%f, l => () }, $class; } sub keep {$_[0]->{f}{$_[1]->{oid}}=dump($_[1]); print "sto: ".dump($_[ +1])."\n";} sub STORE { my ($this, $key, $value)=@_; return $this->{l}{$key}=($value); } sub FETCH { my ($this, $key)=@_; return $this->{'l'}{$key} if exists $this->{'l'}{$key}; print "trying f's $key: $this->{f}{$key}\n"; $this->{'l'}{$key}=eval($this->{f}{$key}); return $this->{'l'}{$key}; } sub FIRSTKEY { my $a = scalar keys %{$_[0]->{l}}; each %{$_[0]->{l}}; +} sub NEXTKEY { each %{$_[0]->{l}}; } sub EXISTS { my ($this, $key)=@_; return exists $this->{f}{$key};} sub DELETE {my ($this, $key)=@_; delete $this->{l}{$key}; delete $this->{f}{$key}; } sub CLEAR {my ($this)=@_; undef %{$this->{l}}; undef %{$this->{f}}; } } $p=Pwo->Pwo("tst.db" ); $p->flush();
I'm using perl, v5.6.1 built for MSWin32-x86-multi-thread

Binary build 631 provided by ActiveState Tool Corp. http://www.ActiveState.com
Built 17:16:22 Jan 2 2002

Replies are listed 'Best First'.
Re: TIEHASH hiccup?
by Juerd (Abbot) on Mar 19, 2002 at 06:23 UTC

    sub FETCH { my ($this, $key)=@_; return $this->{'l'}{$key} if exists $this->{'l'}{$key}; print "trying f's $key: $this->{f}{$key}\n"; $this->{'l'}{$key}=eval($this->{f}{$key}); return $this->{'l'}{$key}; } Can't use an undefined value as a HASH reference at t2.pl line 27.<BR> +<BR> Why does pwotie's FETCH work ok with $o{0} called first, but fail when + $o{0}->{hi} is called first?

    It doesn't work, because $o{0} is undefined, and you're trying to use that as a hash reference. Remember that when you use a tied hash, you can't count on complex vivification. You'll have to initialise a hash ref yourself:

    sub FETCH { my ($this, $key) = @_; return $this->{l}{$key} if exists $this->{l}{$key}; # I don't know what you're doing, but eval is scary :) # Consider using a sub ref (sub { ... } or \&subname) instead $this->{l}{$key} = eval $this{f}{$key}; # Create a new hash ref if the eval returned undef $this->{l}{$key} = { } if not defined $this->{l}{$key}; return $this->{l}{$key}; }

    U28geW91IGNhbiBhbGwgcm90MTMgY
    W5kIHBhY2soKS4gQnV0IGRvIHlvdS
    ByZWNvZ25pc2UgQmFzZTY0IHdoZW4
    geW91IHNlZSBpdD8gIC0tIEp1ZXJk
    

      Right, well this would be true if $this->{l}{$key} were undefined (which it isn't). In fact, correct behavior for FETCH is to return undefined if $key doesn't reference a defined object.

      $o{0} should be defined because ->{f}{0} is defined.

      And the evals are just there to undo Data::Dump -- I'll probably switch to Storable later when all is more stable and I don't need to inspect my objects manually.

      Thanks for the stab at it, tho.

Re: TIEHASH hiccup?
by rjray (Chaplain) on Mar 19, 2002 at 01:15 UTC

    My first guess is that the first line works because of auto-vivification. In that line, you refer to $o{0} without trying to use the value as a hash reference itself. This triggers FETCH, but doesn't actually use the returned value anywhere. After this initial call to FETCH it appears to work (according to your testimony-- I don't have a MSWin32 box to try and reproduce this on).

    In the second case, you are trying to directly utilize the value from $o{0} as a hash reference. I'm not sure what part of your FETCH routine is the source of the problem, but that would be the place to start looking.

    --rjray

      Right... I'm tracking it down to flakiness in the Win32 GDBM. It's a curious bug, though -- something of a Heisenberg bug -- it's only there when you measure it. I tried it on by FreeBSD box and it worked fine.
TIEHASH: Solved
by jhanna (Scribe) on Mar 19, 2002 at 21:54 UTC
    Ok... This is totally weird. There must be a memory bug with the Win32 GDBM_File, but this is the correct FETCH to make my code work, in case anybody cares...

    sub FETCH { my ($this, $key)=@_; return $this->{'l'}{$key} if exists $this->{'l'}{$key}; my $v=eval($this->{f}{$key}); return $this->{'l'}{$key}=$v; }
    For some reason, putting the eval'd structure in a my'd variable clears up the vivifacation issue. Go figure...