Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

A Tied hash returning a tied scalar which becomes untied, why?

by Withigo (Friar)
on Jun 07, 2006 at 08:10 UTC ( #553982=perlquestion: print w/replies, xml ) Need Help??
Withigo has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,
I am having a bit of a problem with the following code--I am creating a tied scalar on the fly from within the FETCH method of a tied hash, and trying to return that tied scalar. I can get the tied scalar to be returned with a bit of cleverness, but then the tied scalar appears to no longer be tied. Even though ref tells me that it is a 'Tie::Scalar', but tied returns undef meaning it's not tied. For further wackiness, I can manually call $tied_scalar->FETCH on the tied scalar and it works, but just $tied_scalar does not automatically call fetch. Would anyone happen to know what is going on here and how I can keep the tied scalar from untying itself?

The general idea I'm trying to get working:
tie our %conf, 'My::Hash'; # $dir is a tied Tie::Scalar our $dir = $conf{'dir'}; print $dir, $/; # prints /home/foo for example # using tie magic, storing a value in $dir also updates # 'dir' in %conf $dir = '/somewhere/else'; $dir = $conf{'dir'}; print $dir, $/; # now prints /somewhere/else
I want %conf to be a general wrapper that can be treated like a hash, so tie seems like the way to go. I also want to fetch values from %conf, store them in scalars, and then when those scalars are changed I would like transparent changes to occur back in the tied hash. Kind of confusing, I know. But I need it this way, so lets just suspend alternative designs and look at it as a pure exercise in Perl.
package My::Hash; use base 'Tie::Hash'; our %hash = ('dir' => '/home/foo'); sub TIEHASH { return bless \%hash, __PACKAGE__; } sub STORE { my ($self, $key, $val) = @_; $self->{$key} = $val; } sub FETCH { my ($self, $key) = @_; my $val = $self->{$key}; tie my $tied_scalar, 'My::Scalar', $key, $val; return $tied_scalar; } 1; package My::Scalar; use base 'Tie::Scalar'; sub TIESCALAR { my ($self, $key, $val) = @_; return bless {'value' => $val, 'key' => $key , 'fetched' => 0 }, __PACKAGE__; } sub STORE { my $self = shift; my $newval = shift; my $key = $self->{'key'}; My::Hash::hash{$key} = $self->{'value'} = $newval; } sub FETCH { my $self = shift; # this cleverness is done so that Tie::Hash FETCH does not # call Tie::Scalar FETCH when it tries to return the tied # scalar. if ($self->{'fetched'}) { return $self->{'value'}; }else{ $self->{'fetched'} = 1; return $self; } } 1;
Now using the above code something unexpected happens:
tie our %conf, 'My::Hash'; our $dir = $conf{'dir'}; print ref $dir, $/; # prints 'My::Scalar'; print tied $dir ? 1 : 0; # prints 0 print $dir, $/; # prints 'My::Scalar=HASH(0x820f268)' print $dir->FETCH # prints '/home/foo'; # attempts to call $dir->STORE give lvalue errors # # why is $dir untied now yet I can still call tied methods # on it?

Replies are listed 'Best First'.
Re: A Tied hash returning a tied scalar which becomes untied, why?
by bart (Canon) on Jun 07, 2006 at 09:11 UTC
    I can manually call $tied_scalar->FETCH on the tied scalar and it works, but just $tied_scalar does not automatically call fetch.
    I've only skimmed through your long post, but I do get the strong impression you're misunderstanding how tieing works. You see, with a tied variable, you have two, er, "variables": the tied variable, and the object. The object is associated to the tied variable, but only behind the scenes, you don't see it.

    Normally, you don't touch the object, except inside the access methods, like FETCH. If you try to access the tied variable with common perl syntax, perl uses the object to drive how it behaves.

    Got it? The object is not tied.

    use My::Scalar; tie my $x, 'My:Scalar'; my $tieobj = tied $x;
    Whenever you do anything to $x, a method for $tieobj is called internally. You normally don't use $tieobj in plain code.

    Stop messing with $tieobj, it's $x you should be using.

    And yes, if you need it, you can tie a hash item. This should work:

    tie $hash{foo}, 'My:Scalar';

    p.s. And stop using __PACKAGE__ to bless your objects. TIESCALAR is a constructor (a class method), and thus it is called with the class name as the first parameter, not with an object as you seem to be assuming. So your TIESCALAR should look like this instead:

    sub TIESCALAR { my ($class, $key, $val) = @_; return bless {'value' => $val, 'key' => $key , 'fetched' => 0 }, $class; }
Re: A Tied hash returning a tied scalar which becomes untied, why?
by ikegami (Pope) on Jun 07, 2006 at 23:13 UTC

    Tied variables cannot be returned.

    sub mysub { tie my $tied, 'MyClass'; return $tied; # Calls FETCH and returns } # the fetched value. my $not_tied = mysub();

    Similarly, tied variables cannot be assigned.

    tie my $tied, 'MyClass'; my $not_tied = $tied; # Calls FETCH and assigns # the fetched value.

    However, there's no special limitation on references to tied variables.

    sub mysub { tie my $tied, 'MyClass'; return \$tied; # Returns a ref to the tied var. my $ref_to_tied1 = mysub(); my $ref_to_tied2 = $ref_to_tied; print($$ref_to_tied1, "\n"); # Calls $tied's FETCH. print($$ref_to_tied2, "\n"); # Calls $tied's FETCH.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://553982]
Approved by Hue-Bond
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (7)
As of 2018-05-24 20:38 GMT
Find Nodes?
    Voting Booth?