my $r = { subref => sub { return 2 * $_[0] } };
my $tied;
tie $tied, 'TieTest', $r;
# This will perform the pre-action before executing
print $tied->(5);
# These will not
for(0..100){
print $tied->(5), "\n";
}
package TieTest;
sub TIESCALAR{
my $caller = shift;
my $r = shift;
bless $r, $caller;
}
sub FETCH{
my $r = shift;
print "Checking for pause...\n";
return $$r{subref};
}
# This will perform the pre-action before executing
print $tied;
# These will not
for(0..10){
print $tied, "\n";
}
This prints the "extra" message plus a "CODE(0x224e2c)". So FETCH only fails when I attempt to dereference the returned subref.
update: it gives
Checking for pause... Can't use an undefined value as a subroutine reference at test.pl line 8.For perl 5.00504
for(0..100){
my $x = $tied;
print $x->(5), "\n";
}
As far as I can tell, the issue is the fact that the thing has already been FETCH'ed into an anonymous variable within either that scope or an enclosing scope. You can trip the bug even easier by doing the following:
print $tied->(5), $/;
{
print $tied->(10), $/;
}
But, if you assign it to something, then the fetch is going to a different place, as seen by the workaround.
And, it seems to be a check for the constancy of the thing. The following also "fixes" the bug:
print $tied->(5), $/;
$r->{subref} = sub { return 2 * $_[0] }; # This is a new anonymous subref
{
print $tied->(10), $/;
}
Or, more transparently to the calling code:
sub FETCH {
my $r = shift;
print "Checking for pause ...";
return $r->{subref} = $r->{subref};
}
It's definitely a bug and it looks to be a misplaced optimization.
In 5.6.1, even the first call doesn't work (Can't use an undefined value as a subroutine reference).
Using
my $sub = $tied;
print $sub->(5), "\n";
fixed the problem in both places. It seems that $var->() and &{$var}() doesn't check if $var is tied.
Anyway, you don't have to use tie at all:
sub wrap {
my ($r) = @_;
return sub {
print "Checking for pause...\n";
return $r->{subref}->(@_);
};
}
my $tied = wrap($r);
print call $tied, 5;
sub call(@){
my $tied = shift;
return $tied->(@_);
}
print $tied->(5), $/;
On the other hand, the pit bull in me has my teeth into tying variables
Tied variables are pretty slow, and they don't always work due to bugs. That's why I suggested the alternative.
Closest I've been able to come so far is this wrapper
The variable you called $tied isn't tied. That's why it works. If you fix the variable name, you get:
sub call {
my $sub = shift;
return $sub->(@_);
}
You basically took the solution I already posted and put it in a function.
I got rid of the misleading/broken/wrong prototype. Don't use prototypes unless you have a good reason and you understand the problems associated with using them.
package TieTest;
sub TIESCALAR {
my $class = shift @_;
die "Incorrect # of arguments" if @_ % 2;
my $self = bless {}, $class;
$self->_init(@_);
return $self;
}
sub FETCH {
my $self = shift @_;
my $work_around = sub {
my $tgt = shift @_;
if (exists $self->{$tgt}) {
print "Fetching value from cache\n";
return $self->{$tgt};
}
print "Calculating and caching value for $tgt\n";
return $self->{$tgt} = $self->{subref}->($tgt);
};
return $work_around;
}
sub _init {
my $self = shift @_;
my %arg = @_;
for (qw/subref/) { # All valid args
$self->{$_} = delete $arg{$_};
}
if (keys %arg) {
my $bad = join ' ', keys %arg;
die "The following args are invalid: $bad";
}
return;
}
package main;
tie my $tied_func, 'TieTest', subref => sub {$_[0] * 2};
# Calculate the function values for 1 .. 100
print $tied_func->($_), "\n" for 1 .. 10;
# Retrieve cached values
print $tied_func->($_), "\n" for 1 .. 10;
sub FETCH {
my $self = shift @_;
my $work_around = sub {
my $tgt = shift @_;
print "Checking for pause\n";
return $self->{subref}->($tgt);
};
return $work_around;
}
I hope that helps. You should still file a bug report!
Cheers - [Limbic~Region|L~R]
That doesn't work in 5.6.1. I don't know if that's a problem.
Cheers - L~R
You are correct, caching isn't always a solution. You might want to check out Memoize for future reference. In many cases, cache is the right solution but with limited resources you need to expire unused items from the cache and only keep the most used recent. The CPAN has all kinds of options. Just search for cache.
The argument checking is not perfect. There are much better modules such as Params::Validate. Everyone has their own way of doing things. Figuring out what works for you and those responsible for maintaining your code is what is important.
This problem is not restricted to ActiveState so that's not the proper course of action. If you are not familiar with perlbug then read up on it and send in a minimalistic test case.
Cheers - L~R
perlmonks.org content © perlmonks.org and dragonchild, ikegami, japhy, Joost, Limbic~Region, rational_icthus
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03