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

Greetings wise monks of perl :)

I'm attempting to access package variables dynamically from other packages. My explanation and terminology may not very clear, so here are some code snippets which show what I am attempting to do.

A package which represents a model subclass.

package my::model::subclass; use strict; use warnings; use base qw/my::model/; our %normal = ( top_level_key => { name1 => value1, name2 => value2, }, ); # the rest of the package...
The base model package
package my::model; use strict; use warnings; sub use_subclass_package_var { my ($self, @cols) = @_; my $normal = join '::', ref $self, 'normal'; foreach my $col (@cols) { # # I need help getting the next line to work properly # next unless exists eval { %{$normal{$col}} }; # the rest of the code } }

Example usage:

my $obj = my::model::subclass->new(); $obj->use_subclass_package_var('top_level_key');

So what I am trying to do here in use_subclass_package_var is check for the key $col in the hash %my::model::subclass::normal, i.e. this would be true if ($col eq 'top_level_key').

I came across How to reference variables in another package via keyword names which appears to similiar to my situation, but my monk mojo is not strong enough yet understand it fully.

Replies are listed 'Best First'.
Re: Using package variables dynamically
by jdporter (Paladin) on Feb 08, 2005 at 18:39 UTC
    Well, generally, packages should keep their hands off of other packages' variables. It's considered unsafe. Instead, you could have (in each subclass) a class-level method which returns a reference to the desired "private" variable. E.g.
    package my::model::subclass; . . . sub normal_hr { # because it returns a hashref \%normal } # then: package my::model; . . . sub use_subclass_package_var { my ($self, @cols) = @_; my $normal = $self->normal_hr; foreach my $col (@cols) { next unless exists $normal->{$col}; # the rest of the code } }
    But I'm confused as to what you're really trying to achieve here. My gut instinct is that whatever it is, it's probably better done another way.
      Great idea. I should have kept in mind the principle of encapsulation when solving this problem. Thank you jdporter, and thanks chromatic.
Re: Using package variables dynamically
by nobull (Friar) on Feb 08, 2005 at 18:32 UTC
    I'm attempting to access package variables dynamically

    That sounds like you are saying you want symrefs.

    but my monk mojo is not strong enough yet understand it fully.

    Symrefs are found in the fobidden books. If you read about them I strongly recommend that you do not lick your fingers when you turn the pages.

    # I need help getting the next line to work properly next unless exists eval { %{$normal{$col}} };

    Note the syntax of dereferecing symbolic references is exactly, completely, totally, 100% the same as the syntax for real references. You shouldn't consider using symrefs until you are well experienced in Perl, and you can't be well experienced in Perl without understanding real references.

    In other words in the above code you have a sympolic reference $normal='my::model::subclass::normal' but you can dereference exactly as if you had a real one $normal=\%my::model::subclass::normal.

    No, seriously, telling you how to get that line working with your current level of understanding would not be helping you.

Re: Using package variables dynamically
by chromatic (Archbishop) on Feb 08, 2005 at 18:35 UTC

    What would happen if you made a class method that could either return a hash reference containing the appropriate keys or check to see if the class knew about the appropriate keys? Then you wouldn't need the globals.

Re: Using package variables dynamically
by revdiablo (Prior) on Feb 08, 2005 at 18:41 UTC

    I'm a bit more care-free with the forbidden knowledge than nobull, so I'll give you an example. Note, this doesn't pass strict. That should be a big red flag, but here it is:

    { package a; our $foo = 1; } { package b; my $otherpkg = "a"; my $varname = "$otherpkg\::foo"; print ${ $varname }, "\n"; # can be simplified as: print $$varname, "\n"; # or the temp variable can be avoided: print ${ "$otherpkg\::foo" }, "\n"; }

    Another solution would be to create an accessor sub in the package, and eliminate the package variable altogether.

    use strict; { package a; my $foo = 1; sub foo { $foo } } { package b; my $otherpkg = "a"; print $otherpkg->foo, "\n"; }