Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

Re: How can I access package variables in a dynamic way

by davido (Cardinal)
on Feb 12, 2019 at 17:48 UTC ( #1229826=note: print w/replies, xml ) Need Help??

in reply to How can I access package variables in a dynamic way

If you absolutely positively must do it, then do it in the narrowest scope possible, and in a layer that falls outside your primary script, so that the script itself doesn't have to deal with the nastiness of messing around in the symbol table. A better abstraction provides more opportunity to test your work, too. This is particularly useful when your work is involved in high-risk behavior.

package Person; use warnings; use strict; our $address_1 = "foo"; our $address_2 = "bar"; our $address_3 = "baz"; 1; package MyPerson; use strict; use warnings; use Data::Alias; my %address; foreach my $var (keys %Person::) { next unless $var =~ m/^address_(\d+)$/; alias $address{$1} = do { no strict 'refs'; ${"Person::${var}"} }; } sub get_address { return $address{shift()} } sub exists_address { return exists $address{shift()} } sub set_address { my ($num, $value) = @_; return $address{$num} = $value; } 1; package main; use strict; use warnings; print "address_1: ", MyPerson::get_address(1), "\n"; print "Setting address_1 to foofoo\n"; MyPerson::set_address(1, 'foofoo'); print "address_1 now contains: ", MyPerson::get_address(1), "\n"; print "Because we created an alias, \$Person::address_1 also changed t +o ", $Person::address_1, "\n\n"; print "Iterate through all Person address_Ns\n"; for (1..3) { print MyPerson::get_address($_), "\n"; }

The output:

address_1: foo Setting address_1 to foofoo address_1 now contains: foofoo Because we created an alias, $Person::address_1 also changed to foofoo Iterate through all Person address_Ns foofoo bar baz

We probably could have done away with the intermediate hash in MyPerson, but using it allows us to ONLY deal with the symbol table of Person in one place in the code. The rest of MyPerson just deals with a run-of-the-mill hash. And package main only deals with some getter and setter functions that accept numbers. We could also have used an array instead of a hash as our alias point, which makes sense if the _number values are contiguous.

Using a variable as a variable name often reeks of the wet paint that has painted you into a corner. But sometimes we're not the ones who did the painting, and aren't able to alter the package we're using (Person, in this case). When that happens, about the best we can do is find a way to encapsulate the ugliness into the narrowest place as is practical, and then to expose a saner interface for our calling code to consume. This code snippet above attempts to do that; to build a bridge out of the painted-in corner by exposing a simple interface that hides away the ugliness.


Replies are listed 'Best First'.
Re^2: How can I access package variables in a dynamic way
by stevieb (Abbot) on Feb 12, 2019 at 20:52 UTC

    This is a very nice, elegant way to handle an unfortunately bad situation.

Re^2: How can I access package variables in a dynamic way
by bangor (Monk) on Feb 12, 2019 at 18:23 UTC
    Great advice, thank you Dave! I will give this a go.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1229826]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (10)
As of 2019-05-24 17:57 GMT
Find Nodes?
    Voting Booth?
    Do you enjoy 3D movies?

    Results (151 votes). Check out past polls.

    • (Sep 10, 2018 at 22:53 UTC) Welcome new users!