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

My problem is that lvalue has issues with temporary values which is what an element of a tied hash of a hash-blessed class attribute returns when accessed. So the following code is fine
{ package foo; # not tied so all is well my %h = qw(one two three four); sub new { bless { val => \%h } } sub val : lvalue { $_[0]->{val}->{one} } } use Devel::Peek; my $obj = foo->new; $obj->val = "foo"; Dump( $obj->{val}->{one} ); __output__ SV = PV(0x80f9c40) at 0x80f9964 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x80f9018 "foo"\0 CUR = 3 LEN = 4
Now if I tie() my hash then the lvalueed method val() will die when called
{ package foo; use Tie::IxHash; tie(my %h, 'Tie::IxHash', qw(one two three four)); sub new { bless { val => \%h } } sub val : lvalue { $_[0]->{val}->{one} } } use Devel::Peek; my $obj = foo->new; Dump( $obj->{val}->{one} ); # dies here $obj->val = "foo"; __output__ SV = PVMG(0x8131170) at 0x8154b68 REFCNT = 1 FLAGS = (TEMP,GMG,SMG,RMG) IV = 0 NV = 0 PV = 0 ... many more lines of output Can't return a temporary from lvalue subroutine at - line 9
Firstly why does this happen, and secondly is there a way around it?
TIA

_________
broquaint

Replies are listed 'Best First'.
Re: lvalue trickery
by Abigail-II (Bishop) on Jul 26, 2002 at 13:12 UTC
    Looking at the FETCH subroutine of Tie::IxHash solves the problem:
    sub FETCH { my($s, $k) = (shift, shift); return exists( $s->[0]{$k} ) ? $s->[2][ $s->[0]{$k} ] : undef; }
    You haven't stored anything in the hash yet, so $_[0]->{val}->{one} returns undef, which isn't an lvalue.

    Suggested and untested solution: $h {one} = undef after the tie.

    Abigail

      You haven't stored anything in the hash yet, so $_[0]->{val}->{one} returns undef, which isn't an lvalue.
      Erm ...
      { package foo; use Tie::IxHash; tie(my %h, 'Tie::IxHash', qw(one two three four)); sub new { bless { val => \%h } } sub val : lvalue { $_[0]->{val}->{one} } } use Data::Dumper; my $obj = foo->new; print Dumper( $obj ); __output__ $VAR1 = bless( { 'val' => { 'one' => 'two', 'three' => 'four' } }, 'foo' );
      I figured that the issue might be in the FETCH method of Tie::IxHash, but I thought that tied variables were meant to behave just like normal variables (in terms of interface at least), apparently not.

      _________
      broquaint

Re: lvalue trickery solution
by shotgunefx (Parson) on Jul 26, 2002 at 16:56 UTC
    I think the problem is that the %h you are referencing is gone (it's pad) before val is called. changing new to have a new copy of %h and fixing val to end with a a reference fixes it.
    update
    It does fix it but not because of garbage collection. See answer in reply below.
    #!/usr/bin/perl -w use strict; use Data::Dumper; { package foo; use Tie::IxHash; use Data::Dumper; tie(my %h, 'Tie::IxHash', qw(one two three four)); sub new { my %newh = %h; bless { val => \%newh } } sub val : lvalue { my $r = \$_[0]->{val}{one}; $$r } } use Devel::Peek; my $obj = foo->new; Dump( $obj->{val}->{one} ); # dies here $obj->val = "foo"; warn Dumper($obj);


    -Lee

    "To be civilized is to deny one's nature."
      I think the problem is that the %h you are referencing is gone (it's pad) before val is called. changing new to have a new copy of %h and fixing val to end with a a reference fixes it.
      The %h is fine because it's refcnt never dropped below 1. But referencing the value of the hash works a treat! I can now lvalue assign my tied hash elements of blessed-hash object attributes to my hearts content, many many thanks shotgunefx!

      _________
      broquaint

        Your welcome. Chock the %newh up to cruft. Originally I thought that was the problem. Thinking it was referencing a value that was getting GC'ed. Copying %h suppressed the "Can't return a temporary from lvalue subroutine" but it just failed silently. Then I fixed val so you are correct. The temp assignment is not neccassary.

        -Lee

        "To be civilized is to deny one's nature."
Re: lvalue trickery
by RMGir (Prior) on Jul 26, 2002 at 12:53 UTC
    Hmmm, in the "untied" branch, you assign to $obj->val before the dump; you do it after in the version that dies.

    Maybe that's the bug? It might be an auto-vivification issue, more than a tie issue.

    That's a wild guess, though.
    --
    Mike

      Hmmm, in the "untied" branch, you assign to $obj->val before the dump; you do it after in the version that dies.
      Well I can't do the Dump after the $obj->val assigment as that's where it dies. I don't believe it's an auto-vivification issue as there's nothing that's being auto-vivfied and if you Data::Dumper the object you can see that both tied and untied are exactly the same.
      HTH

      _________
      broquaint

        Well I can't do the Dump after the $obj->val assigment as that's where it dies.
        oh yes, you can:
        $SIG{__DIE__} = sub { Dump( $obj->{val}->{one} ); die $_[0]; }; { package foo; use Tie::IxHash; tie(my %h, 'Tie::IxHash', qw(one two three four)); sub new { bless { val => \%h } } sub val : lvalue { $_[0]->{val}->{one} } } use Devel::Peek; my $obj = foo->new; # dies here $obj->val = "foo";
        still, this doesn't solve anything :-)

        cheers,
        Aldo

        __END__ $_=q,just perl,,s, , another ,,s,$, hacker,,print;
        Ah, missed that. I thought the "many more lines of 0's" on the dump was where it was crashing.

        Do you have the same problem if you use another Tie module?
        --
        Mike

Re: lvalue trickery
by Anonymous Monk on Jul 26, 2002 at 23:24 UTC
    sub val : lvalue { $_[0]->{val}->{one} }
    change it to:
    sub val : lvalue { $_[0]->{val}->{one} = undef unless exists $_[0]->{val}->{one}; $_[0]->{val}->{one}; }