Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

How to change referent of lexical reference?

by autarch (Hermit)
on Sep 21, 2003 at 19:52 UTC ( [id://293027]=perlquestion: print w/replies, xml ) Need Help??

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

Ok, here's what I want to do in pseudo-code:

my $foo = { hash => 'ref' }; my $bar = { hash => 'ref' }; some_sub($foo); $foo == $bar ...; # should now be true! sub some_sub { my $ref = shift; $ref = $bar; # except in Perl this just copies over # lexical $ref, not $foo! }

The problem is compounded by the fact that I don't control the call to some_sub().

In fact, in this case some_sub() is really a STORABLE_thaw() method and I'd like to take the reference Storable is giving me and make it instead point elsewhere, to an already existing reference.

I suspect this can't be done in pure Perl. I've looked at both Devel::LexAlias and Lexical::Alias, but neither one does what I want.

Hints of how to this in XS are welcome.

Replies are listed 'Best First'.
Re: How to change referent of lexical reference?
by blokhead (Monsignor) on Sep 21, 2003 at 20:01 UTC
    You can modify any arguments (references or ordinary scalars) passed to a sub by editing them inplace in @_ (as opposed to making a copy with shift inside the sub). I can't tell from your description if this is possible in your case, but it's a thought:
    my $foo = \"foo"; my $bar = \"bar"; print "$$foo\n"; change_ref($foo); print "$$foo\n"; sub change_ref { $_[0] = $bar; } __OUTPUT__ foo bar
    I would assume there's a corresponding way to do it in XS too, but I'm not the one to ask about that.

    blokhead

      Hmm, I just tried that but it didn't work. The STORABLE_thaw() method gets called from Storable's XS code, which may explain the problem.

      I think that Storable calls STORABLE_thaw with a blessed reference of the appropriate type (in my case a hash ref) and then looks directly at the _referenced_ data after calling the method, as opposed to looking at the reference itself.

      So I need a way to switch the referent out from under the reference.

Re: How to change referent of lexical reference?
by sandfly (Beadle) on Sep 21, 2003 at 22:57 UTC
    I had a similar experience with Storable. Is there a big problem with simply cloning bar: %$ref = %$bar? Alternatively, what about making $ref into a proxy for $bar:

    { package Proxy; our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $name = $AUTOLOAD; $name =~ s/.*://; return if $name eq 'DESTROY'; $self->{bar}->$name(@_); } sub isa { (shift)->{bar}->isa(@_) } sub can { (shift)->{bar}->can(@_) } } sub some_sub { my $ref = shift; %$ref = (bar => $bar); bless $ref, "Proxy"; }
    This code is untested, but I've had success with similar techniques. It assumes that $ref is a hash reference-type object, and that you never access the contents of the hash directly.
      In fact, I specifically want to avoid cloning, because for a given set of unique attributes (in this case, a database table primary key), I don't want there to be more than one object representing that unique row.
Re: How to change referent of lexical reference?
by BrowserUk (Patriarch) on Sep 22, 2003 at 04:54 UTC

    If I understand you, you want to be able to freeze two (or more) references to the same data or datastructure and when you thaw them, have both references again refer to a single copy of the referent, rather than two separate copies.

    For this to happen, you need to ensure that you pass both (all) of the data to Storable::freeze() at the same time. This is the only way that it will be able to determine that the references do indeed refer to the same data.

    #! perl -slw use strict; use Data::Dumper; use Storable qw[freeze thaw]; my $hashref = { my=>'data' }; my $refA = \$hashref; my $refB = \$hashref; { my @frozen = map{ freeze $_ } $refA, $refB; my( $refA_thawed, $refB_thawed ) = map{ thaw $_ } @frozen; print Dumper $refA_thawed, $refB_thawed; } { my @data = ( $refA, $refB ); my $frozen = freeze \@data; my $thawed = thaw $frozen; my ($refA, $refB) = @$thawed; print Dumper $refA, $refB; } __END__ $VAR1 = \{ 'my' => 'data' }; $VAR2 = \{ 'my' => 'data' }; $VAR1 = \{ 'my' => 'data' }; $VAR2 = $VAR1;

    In the first anon. block, the two references are frozen in separate calls to freeze() and thaw(), and so Storable has no way to recognise that they refer to the same data, and when they are thawed, they are pointed at separate copies of the data.

    However, in the second block, the two references are passed to freeze() during the same call. This is achieved by simply wrapping them into a common data structure (@data). This way, you are giving Storable the opportunity to recognise that the both references refer to the same data, and it does the appropriate thing accordingly.

    Now when the data is thawed(), the resultant references again point to a single copy of the referent.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
    If I understand your problem, I can solve it! Of course, the same can be said for you.

      If I understand you, you want to be able to freeze two (or more) references to the same data or datastructure and when you thaw them, have both references again refer to a single copy of the referent, rather than two separate copies.

      No, that's not what I want. I want to be able to serialize an arbitrary data structure, and then every time it's thawed I want to check if an existing data structure in memory matches the thawed one, and silently tell Storable to simply use the existing one instead.

      I have no control over when or how things might be frozen, so your solution, while interesting, isn't really relevant.

        Sorry about that, though if your original question had been stated as clearly as here, then it might have saved a few false impressions:)

        The best suggestion I can make is that you keep a hash, with frozen copies of the in-memory datastructures that might match as the keys, and a reference to them as the values.

        When you import a frozen datastructure, you then just look it up in the hash and if you find a match, set the pointer to the new one to the value from the hash, and there would be no need (or point) in asking Storable to thaw the inbound structure. If not, just thaw() as normal.

        This would mean maintaining the frozen copies as the get modified, but this could be easily done by tieing the structures that make up each compound structure, and updating the frozen copy, cash hash on STORE.

        This arrangment would make for fast isolation of matching structures at the expense of the overhead of re-freezing when elements of the structure are updated.

        If you really wanted to trim that overhead, you could start playing games with arrays of lvalue refs into the frozen copy and overwrite individual fields, packing the data yourself, but this would require some effort to set up for the general case and you'll probably consider that overkill.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
        If I understand your problem, I can solve it! Of course, the same can be said for you.

Re: How to change referent of lexical reference?
by tilly (Archbishop) on Sep 21, 2003 at 23:55 UTC
    I would be inclined to just define my own comparison method that made it obvious that I was doing a deep comparison that the two hashes had the same contents.

    If you insist on being able to use == for this, you could try turning $foo into an overloaded object whose == method checked that both sides freeze into the same string (with $Storable::Canonical set to a true value of course, and modulo what you need to do for the overload to operate).

      See above. This isn't a question of comparison, but rather that I really don't want two objects that have the same data.
        The general part of the question (how to recognize other data structures that mirror data) is hard.

        But causing one reference to a data structure to be the same as another is fairly easy:

        sub some_sub { $_[0] = $bar; }
        The trick is that while variables are in @_ they are passed in by reference. If you don't make copies, then you can modify the passed in values. This API may surprise...
Re: How to change referent of lexical reference?
by autarch (Hermit) on Sep 23, 2003 at 04:40 UTC
    So it turns out what I want can be done like this in XS:
    void
    substitute_ref(ref1, ref2)
         SV* ref1
         SV* ref2;
    
         PPCODE:
           sv_unref(ref1);
           ref1 = ref2;
    
    
    Yep, it's that simple. Still, I wish I could do it from pure Perl.
      Doh, this didn't work, I was just confused.

      I think that the only way to do this is to somehow make the underlying SVs point to the same hash structure, but I don't know how to do that safely (memory-wise), and Perl certainly has no API for this.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://293027]
Approved by jdtoronto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (2)
As of 2024-04-20 03:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found