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:
This is the output when it is executed:use strict; use Remember; my $fred; $x = tie $fred, "Remember", "camel.log"; $fred = 1; $fred = 4; $fred = 5; untie $fred; system "cat camel.log";
So far, so good. Let's add an extra method to theThe Start 1 4 5 The End
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:
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 "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";
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:
To get the program to work properly and silence the warning, eliminate any extra references to the tied object before callinguntie attempted while 1 inner references still exist
untie. You can do that explicitly:
Often though you can solve the problem simply by making sure your variables go out of scope at the appropriate time.undef $x; untie $fred;