A Subtle Untying Trap
If you intend to make use of the object returned from tie
or tied
, and the class defines a destructor, there is a subtle trap you must guard against. Consider this (admittedly contrived) example of a class that uses a file to log all values assigned to a scalar:
package Remember; sub TIESCALAR { my $class = shift; my $filename = shift; open(my $handle, ">", $filename) or die "Cannot open $filename: $!\n"; print $handle "The Start\n"; bless {FH => $handle, VALUE => 0}, $class; } sub FETCH { my $self = shift; return $self->{VALUE}; } sub STORE { my $self = shift; my $value = shift; my $handle = $self->{FH}; print $handle "$value\n"; $self->{VALUE} = $value; } sub DESTROY { my $self = shift; my $handle = $self->{FH}; print $handle "The End\n"; close $handle; } 1;
Here is an example that makes use of our
Remember
class:
use strict; use Remember; my $fred; $x = tie $fred, "Remember", "camel.log"; $fred = 1; $fred = 4; $fred = 5; untie $fred; system "cat camel.log";
This is the output when it is executed:
The Start 1 4 5 The End
So far, so good. Let's add an extra method to the
Remember
class that allows comments in the file--say, something like this:
sub comment { my $self = shift; my $message = shift; print { $self->{FH} } $handle $message, "\n"; }
And here is the previous example, modified to use the
comment
method:
use strict; use Remember; my ($fred, $x); $x = tie $fred, "Remember", "camel.log"; $fred = 1; $fred = 4; comment $x "changing..."; $fred = 5; untie $fred; system "cat camel.log";
Now the file will be empty, which probably wasn't what you intended. Here's why. Tying a variable associates it with the object returned by the constructor. This object normally has only one reference: the one hidden behind the tied variable itself. Calling "
untie
" breaks the association and eliminates that reference. Since there are no remaining references to the object, the DESTROY
method is triggered.
However, in the example above we stored a second reference to the object tied to $x
. That means that after the untie
there will still be a valid reference to the object. DESTROY
won't get triggered, and the file won't get flushed and closed. That's why there was no output: the filehandle's buffer was still in memory. It won't hit the disk until the program exits.
To detect this, you could use the -w
command-line flag, or include the use warnings "untie"
pragma in the current lexical scope. Either technique would identify a call to untie
while there were still references to the tied object remaining. If so, Perl prints this warning:
untie attempted while 1 inner references still exist
To get the program to work properly and silence the warning, eliminate any extra references to the tied object before calling
untie
. You can do that explicitly:
undef $x; untie $fred;
Often though you can solve the problem simply by making sure your variables go out of scope at the appropriate time.