I want to stash a value into a package variable. Complications: The stashee is a different package from "me", __PACKAGE__; and at compile time, I only have the stashee's package name as a variable. So this won't work:

$Teddy::foo = 'cash';

It won't work because I don't know 'Teddy' when I'm writing the code.

Here's a little demo script that DWIW:

use strict; use warnings; use feature 'say'; { package Teddy; } package main; no strict 'refs'; my $ns = 'Teddy'; my $glob = *{"${ns}\::foo"}; say ' $glob: ', $glob; say '$Teddy::foo: ', $Teddy::foo; my $cram = 'cheese'; say ' $cram: ', $cram; ${ *{"${ns}\::foo"} } = $cram; say '$Teddy::foo: ', $Teddy::foo; my $yank = ${ *{"${ns}\::foo"} }; say ' $yank: ', $yank; __DATA__ Output: $glob: *Teddy::foo Use of uninitialized value $Teddy::foo in say... $Teddy::foo: $cram: cheese $Teddy::foo: cheese $yank: cheese __END__

The important code is ${ *{"${ns}\::foo"} }. The stashee (here 'Teddy') is stored in the variable $ns. Although Teddy is completely empty and innocent of anything, the cheese gets put in his belly.

Note that this is a somewhat strange thing to do, definitely nonstandard. It uses a symbolic reference and a package variable and it pollutes Teddy's namespace aggressively. But it does work.

The reason for this, in short, is that my code is going to do sekret stuff that must be parameterized at compile time. At run time, caller will call my code without the opportunity to pass the parameter explicitly. If I store the value of that parameter inside my own code, then either I have to make its value global to all callers or I have to store it in a hash keyed to caller. That last idea risks a memory leak, since I don't have any real way to know when caller has absolutely finished executing. Stashing the parameter in caller's namespace at compile time means I can drag it out later without ill effect and it will be destroyed when caller exits.

Note that although the demo script will run under -W, actual code probably will not (mine doesn't). The scope that stashes is a different scope from that which grabs; otherwise why bother? So when the stashing sub exits after the line:

    ${ *{"${ns}\::foo"}  }   = $cram;

... the warning is emitted: Name "Teddy::foo" used only once: possible typo...

Any actual code that does the stash will need:

no strict 'refs'; # disable complaint about symbolic referen +ce no warnings 'once'; # disable complaint about var only used on +ce ${ *{"${ns}\::foo"} } = $cram;

... after which both strict and warnings should be re-enabled.

Updates:

2010-05-30

Fix all the clever stash code (per, IIRC, bart) to eliminate the concatenation.

Restore, with some trepidation, the paragraph of rationale.

Explain the warnings issue.

2010-05-31

Fixed dropped right braces per almut.

2010-06-25

Well, an interesting battle between the symbolic ref and the symbol table access. Meanwhile, in my current code, I've gone to the %state_of approach after all, in which I don't stash into caller's namespace but instead store in my own pseudo-global lexical.

On reflection (rarely unwise), I realized that nobody is going to use() my module so many times in one script that allocating (and never releasing) a little state for it will consume an appreciable amount of memory. It is, technically, a memory leak and one might consider that if one were to execute in a persistent environment.

You may still have a desire to stash; if so, you're welcome.

- the lyf so short, the craft so long to lerne -

Replies are listed 'Best First'.
Re: Stashing a Package Variable by Reference
by almut (Canon) on May 30, 2010 at 10:46 UTC
    ${ *{${ns} . '::foo'} } = $cram;

    You could also write

    ${ $::{$ns.'::'}{foo} } = $cram;

    in which case you wouldn't need the no strict 'refs'.

    #!/usr/bin/perl -l use strict; use warnings; my $ns = 'Teddy::'; ${ $::{$ns}{foo} } = 'cheese'; print '$Teddy::foo: ', $Teddy::foo; # or the other way round $Teddy::foo = 'bar'; print '${ $::{$ns}{foo} }: ', ${ $::{$ns}{foo} }; __END__ $Teddy::foo: cheese ${ $::{$ns}{foo} }: bar

    The main stash %:: holds an entry 'Teddy::', whose value is the stash of that package (or more precisely, a glob whose hash part is the stash).  The latter holds an entry 'foo', whose value is a glob, the scalar slot of which you can select with ${ ... }

    print grep /Teddy/, keys %::; # Teddy:: print $::{'Teddy::'}; # *main::Teddy:: print keys %{$::{'Teddy::'}}; # foo print $::{'Teddy::'}{foo}; # *Teddy::foo


    P.S.: you could also put a direct reference to a string 'cheese' in the scalar slot of the glob, in which case you would get a kind of read-only variable:

    $::{'Teddy::'}{foo} = \'cheese'; print $Teddy::foo; # cheese $Teddy::foo = 'bar'; # "Modification of a read-only value attempted a +t ..."

      Your code is, I admit, more clever than mine. But if I understand correctly, you're setting up a hash in the stasher's symbol table. Since my case isn't really a script, that's not main and I might write something like:

      package Stasher; sub stash { my $ns = caller; $ns = $ns . '::'; ${ $Stasher::{$ns}{foo} } = 'cheese'; };

      Before I hammered out my current approach, I considered a less clever one:

      package Stasher; our %stash_of; sub stash { my $ns = caller; $stash_of{$ns}{foo} = 'cheese'; };

      This resembles a flyweight object and suffers from a similar weakness, in that Stasher keeps %stash_of entries alive unless and until an explict DESTROY is called. That's okay for flyweights but in my case, how does Stasher know when caller's entire package has gone out of scope?

      My thought was to stash $foo in caller's symbol table because I can rely on it being deallocated in Perl's own good time.

      - the lyf so short, the craft so long to lerne -
        But if I understand correctly, you're setting up a hash in the stasher's symbol table. Since my case isn't really a script, that's not main ...

        Not really sure what you mean.  Of course, the approach isn't limited to main — the latter is just the top-level stash that every "package path" is rooted at.

        Essentially, all I wanted to point out is that, in general

        ${ $::{'PackageA::'}{'PackageB::'}{'PackageC::'}{foo} }

        functionally achieves the same as your symbolic reference

        ${ *{"PackageA::PackageB::PackageC::foo"} }

        (which could be simplified to  ${"PackageA::PackageB::PackageC::foo"},  btw — or ${"$ns\::foo"} in your specific case)

        But, as the former approach doesn't involve symbolic references, it's compatible with use strict, i.e. it doesn't need no strict 'refs'.


        P.S.: in several places of the current version of your original node, you're missing the closing curly in the expression ${ *{"${ns}\::foo"}

Re: Stashing a Package Variable by Reference
by ikegami (Patriarch) on May 31, 2010 at 20:40 UTC

    ... after which both strict and warnings should be re-enabled.

    The assignment doesn't produce any warnings. It's the later use of the variable that does. Note that this warning can be avoided if the variable is created at compile time from a different package than the one in which it resides. (See vars.)

    As for strict, you can let Perl re-enable strict by placing the no strict 'refs'; inside of curlies.

    $ perl -le' use strict; use warnings; BEGIN { my $pkg = "Foo::Bar"; my $var = "s"; no strict "refs"; ${"${pkg}::$var"} = 123; } print $Foo::Bar::s; ' 123
Re: Stashing a Package Variable by Reference
by JavaFan (Canon) on May 31, 2010 at 10:26 UTC
    Why the typeglob? Doesn't ${"${ns}::foo"} do the trick?