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

Hello everyone,

I'm currently using Perl 5.12.3. I'm trying to mix tied hashes and overloaded objects, much to my own dismay. I overload the '+' operator in a package, and I store an array reference blessed into that package in a tied hash. Example:

my %hash; tie %hash, 'main'; $hash{'test'} = bless [], 'Test';

Now, when I do the following, I get an error about not having an overload for '0+'!

$hash{'test'} += 3;
These work:
my $test = bless [], 'Test'; $test += 3; my %hash; tie %hash, 'main'; $hash{'test'} = bless [], 'Test'; $hash{'test'} = $hash{'test'} + 3;

I've read the overload documentation. I've tried (with no avail) specifying fallback. I've run it through a debugger, only to discover that it pukes with the '0+' error when attempting to return the value from FETCH. I've read some of the Perl source to try and figure out what's going on. Does anyone here have any insight to what I'm doing wrong? A provable script follows to illustrate what I'm trying to accomplish in code.

Thanks,
Rob
#!/usr/bin/env perl package Test; use overload '+' => \&append; sub append { my ( $self, $value ) = @_; push @$self, $value; return $self; } package main; use strict; use warnings; use Test::More tests => 14; use Test::Exception; sub TIEHASH { my ( $class ) = @_; return bless {}, $class; } sub FETCH { my ( $self, $key ) = @_; return $self->{$key}; } sub STORE { my ( $self, $key, $value ) = @_; $self->{$key} = $value; } lives_ok { my %hash; tie %hash, __PACKAGE__; $hash{'test'} = bless [], 'Test'; isa_ok($hash{'test'}, 'Test'); }; lives_ok { my %hash; tie %hash, __PACKAGE__; my $test = bless [], 'Test'; $test += 3; $hash{'test'} = $test; is(3, $hash{'test'}[0]); }; lives_ok { my %hash; tie %hash, __PACKAGE__; $hash{'test'} = bless [], 'Test'; $hash{'test'} = $hash{'test'} + 3; is(3, $hash{'test'}[0]); }; lives_ok { my %hash; tie %hash, __PACKAGE__; $hash{'test'} = bless [], 'Test'; my $test = $hash{'test'}; $test += 3; $hash{'test'} = $test; is(3, $hash{'test'}[0]); }; lives_ok { my %hash; $hash{'test'} = bless [], 'Test'; $hash{'test'} += 3; is(3, $hash{'test'}[0]); }; lives_ok { my %hash; tie %hash, __PACKAGE__; $hash{'test'} = []; push @{$hash{'test'}}, 3; is(3, $hash{'test'}[0]); }; lives_ok { my %hash; tie %hash, __PACKAGE__; $hash{'test'} = bless [], 'Test'; $hash{'test'} += 3; is(3, $hash{'test'}[0]); }; # ???

Replies are listed 'Best First'.
Re: Mixing tie %hash and overload results in strange behavior
by ikegami (Pope) on Feb 23, 2011 at 20:36 UTC

    These work: $hash{'test'} = $hash{'test'} + 3;

    Yeah, because you overload "+" but not "+=". You need to overload "+=" too.

    Update: While the above it true, there is a bug cause the overload handler for '+=' not to get called. Filed a ticket: RT#84786

Re: Mixing tie %hash and overload results in strange behavior
by ikegami (Pope) on Feb 24, 2011 at 23:17 UTC

    On Thu, Feb 24, 2011 at 9:41 AM, Dave Mitchell wrote:

    This was fixed in a recent blead. Under blead, you get instead:

    ok 1 - +, tied hash ok 2 - +=, normal hash # Operation "=": no method found, argument in overloaded package Test +at /tmp/p line 52. ...

    because with a tie, it can't determine whether its safe to skip using the copy constructor, so it always calls it. Adding one, e.g.

    '=' => sub { bless [ @{$_[0]} ], 'Test' },

    makes all three tests pass.

    So starting with 5.14, you'll be able to do

    use overload '+' => \&append, '+=' => \&append, '=' => => sub { bless [ @{$_[0]} ], 'Test' };