Let's say I want to use
Tie::Hash to have a temporary wrapper around an existent hash until I untie.
In the following example I'm simply returning any value enclosed in < > .
I'm facing some "complications", which make me doubt I'm doing the right thing.
- to access the original data I have to pass the hash-ref again to the constructor,
there doesn't seem to be any reverse tied command helping here, so I need to explicitly store it in the object. °
- When internally accessing the data of the original hash, I have to explicitly untie and retie the hash again, otherwise I'm causing an infinite recursion (yikes)
- I'm getting an untie attempted while 1 inner references still exist unless I undef the object prior,
I'm not sure what this warning is supposed to mean.
And it only cares about other refs in the same scope. SEE UPDATE ²
I'm sure I could kind of avoid all this by copying all data of the original hash when constructing the tie
inside TIEHASH, but this seems like a waste of resources.
Any more elegant approach???
use v5.12;
use warnings;
package main;
my %hash;
@hash{"a".."c"} = 40 ..42; # init
tie # bind wrapper
%hash,
'Data::Proxy::TieHash',
\%hash; # redundant, why?
say $hash{a};
say @hash{"a".."c"};
delete $hash{b};
say @hash{"a".."c"};
untie %hash; # unbind wrapper
say @hash{"a".."c"};
BEGIN {
package Data::Proxy::TieHash;
require Tie::Hash;
use Scalar::Util qw/blessed/;
use Carp;
our @ISA = qw(Tie::ExtraHash);
# All methods provided by default, define
# only those needing overrides
# Accessors access the storage in %{$_[0][0]};
# TIEHASH should return an array reference with the first element
# being the reference to the actual storage
sub _report {
# uncomment to trace
#carp "Doing \U$_[0]\E of $_[1] at $_[2].\n"
};
sub DELETE {
my ($obj, $key) = @_;
my ($meta, $orig) =@$obj;
_report('DELETE', $orig, $key);
my $class = blessed $obj;
undef $obj;
untie %{$orig};
my $ret = delete $orig->{$key};
tie %{$orig}, $class, $orig;
return $ret;
}
sub FETCH {
goto &FETCH1; # use implementation
}
sub FETCH0 {
_report('FETCH', $_[0][1], $_[1]);
untie %{$_[0][1]};
my $ret = $_[0][1]->{$_[1]};
tie %{$_[0][1]}, 'Data::Proxy::TieHash', $_[0][1];
return "<$ret>" if defined $ret;
return undef;
}
sub FETCH1 {
my ($obj, $key) = @_;
my ($meta, $orig) =@$obj;
_report('FETCH', $orig, $key);
my $class = blessed $obj;
undef $obj; # avoid warning
untie %{$orig};
my $ret = $orig->{$key};
tie %{$orig}, $class, $orig;
return "<$ret>" if defined $ret;
return undef;
}
}
<40>
<40><41><42>
Use of uninitialized value in say at proxy_tiehash.pl line 21.
<40><42>
Use of uninitialized value in say at proxy_tiehash.pl line 25.
4042
UPDATES
°) this smells like internally causing a recursive data structure
²) OK I got it https://perldoc.perl.org/perltie#The-untie-Gotcha, untie is normally supposed to trigger DESTROY, but inf there are still other refs to the underlying $object
DESTROY can't be triggered. In my case it's actually better not to trigger DESTROY and better to silence the warning altogether.