It's a long title, but I'm afraid I can't take a single word out without losing the true meaning of the question. I'll give a quick description of what I'm trying to achieve first, then a long rambling on why I want it done this way. Skip the rambling if you intend to directly answer abiding with the question title :-)
Assuming the existence of a lexicalize
builtin that does just what I want, here goes:
package Whatever;
{
lexicalize $Other::Package::var;
local $var = 42;
Other::Package->do_stuff; # depends on that variable internally
}
For a bit of context, I'm whitebox-testing a third-party module.
I want the variable localized because I want it changed for a limited time only, before the tests move on to something else. local
is the best way I found to do this without depending on knowing the module's choice for an initial value.
I want a lexical binding for two main reasons:
I couldn't decently use our
because it won't grab a variable from another package: “No package name allowed for variable $Other::Package::var in "our".” Cheating to temporarily enter Other::Package's scope doesn't cut it: if I use a block ({ package Other::Package; our $var }
) then the binding doesn't last long enough to be useful; and if I don't (package Other::Package; our @var; package main
) then I need to know and copy the previous package's name, which prevents moving that piece of code around too much.
While doing my homework before asking a previous form of this question, I discovered Lexical::Var
, which seemed like it would be exactly what I needed. Alas: “Can't localize through a reference.”
I've also tried my best on my gut feeling of my *var
-based forms, but kept bumping into syntax errors. I've learned more than I cared to about scoping and binding today :-)
I can't think of a reason why what I want shouldn't be possible, but I can't find the right incantation. Am I asking for an unfortunate unimplemented edge case?
If I get you right, all you need is our. Forgive me if I dont.
Here's working example:
#!/usr/bin/env perl
use strict;
use warnings;
package Some;
our $var = 42;
sub do_stuff { return $var }
package main;
{
local $Some::var = 43;
print "Localized: " . Some->do_stuff() . "\n";
};
print "Normal: " . Some->do_stuff() . "\n";
But if you try to localize private (my
) variable of some package, you probably doing something wrong.
there are several ways to do this:
given:
{package Test::Pkg;
our $var = 'init';
sub method {print "Test::Pkg::var = $var\n"}
}
alias a package variable from the current package to the target package.
{
package main;
our $var;
Test::Pkg->method; # Test::Pkg::var = init
local *var = \local $Test::Pkg::var;
# alias $var to a localized $Test::Pkg::var
$var = 'new value';
Test::Pkg->method; # Test::Pkg::var = new value
}
Test::Pkg->method; # Test::Pkg::var = init
localize through a glob reference:
{
package main;
my $var = \*Test::Pkg::var; # globref to target the local
Test::Pkg->method; # Test::Pkg::var = init
local *$var = \'new value'; # fills the scalar slot of the glob
Test::Pkg->method; # Test::Pkg::var = new value
}
Test::Pkg->method; # Test::Pkg::var = init
bind a the lexical in the target package, and let it spill into the current package:
{
package Test::Pkg; # in the target package
our $var; # create the lexical name $var for $Test::Pkg::var
package main; # enter main package, lexical still in scope
Test::Pkg->method; # Test::Pkg::var = init
local $var = 'new value';
Test::Pkg->method; # Test::Pkg::var = new value
}
Test::Pkg->method; # Test::Pkg::var = init
the last example uses a slightly awkward double package declaration to create a lexical in one package and get it into another. This trick is useful with local
in other scenarios:
no strict 'refs';
local ${'Some::Pkg::'.$name} = 'something';
use strict 'refs';
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With