http://qs1969.pair.com?node_id=226165


in reply to Grabbing Variable Names

As pointed out in previous answers this issue is two totally separate subissues depending on whether you want to find lexicals or globals.

For people that know how perl is built up with symbol tables and typeglobs this is an easy match. But I've found that symtables (for short) and typeglobs are something confusing and hard-to-grasp for many. Personally I had to read an extra (non-perldoc) document or two to understand them when I first fiddled with them. perldata certainly wasn't enough. Neither was perlref or perlmod. The documentation needed to solve this problem is so scattered over different documents in perldoc I figured I'd cook up something and give a little step-by-step explanation. It could perhaps be better, but hopefully this gives you some sort of understanding of how symtables and typeglobs can be used in practice.

(After rereading my own post I realized that this turned out to a very compact mini-tutorial on symtables and especially typeglobs. That wasn't intentional. I just kept filling in with explanations as I felt necessary. It's quite possible that it got a little bit too dense.)
# We define this subroutine anonymously and store it in a lexical # scalar to avoid it getting found by itself. my $find_globs; $find_globs = sub { my ($pkg) = @_; my @vars; no strict 'refs'; my @globs = values %$pkg; # The symbol table is made available through a hash. That hash is # the package's name plus two colons. The values in the symbol # table are typeglobs. Typeglobs are holders of the values used # when you access a global variable, like $foo. That accesses # the *foo typeglob's scalar value. Typeglobs are prefixed with # a "*", as you can see. foreach my $glob (values %$pkg) { my $name = *$glob{NAME}; # Each glob also saves its name, next to the variable-data. if ($name =~ /::\z/) { # As you might recall, a symbol table is made available # through a hash which ends in "::". This hash also lives in # a typeglob, and is thus stored in the symbol table. # (Actually, I'm kind of cheating here. There could be # other datatypes defined here than the hash, and perhaps # the hash isn't even defined. The program won't break # if there are; it just won't return non-hashes that end # with "::".) push @vars => $find_globs->("$pkg$name") unless $name eq 'main::'; # From the main package all packages can be reached, even # main itself. That means *main::main:: points to main. You # see where this is leading us: nowhere. So don't follow # any mains. } else { my @types = ( defined $$glob ? '$' : (), defined @$glob ? '@' : (), defined %$glob ? '%' : (), defined &$glob ? '&' : (), ); # Here we see which data types are defined. push @vars => map $_ . *$glob{PACKAGE} . "::$name", @types; # Not only does it save the name, it also saves the package # it lives in. } } return @vars; }; use Data::Dumper; # Just to get extra packages. :) # Here you might want to do() your program file. print "$_\n" for $find_globs->('main::');
A typeglob's variable data can also be reached through subscribing it with the type of data you want (called the *foo{THING} syntax): SCALAR, ARRAY, HASH, CODE, IO, GLOB, (FILEHANDLE). E.g. *foo{ARRAY} gives a reference to @foo. Something to be aware of is that SCALAR always returns a reference. If the scalar slot for that typeglob isn't defined an anonymous scalar reference will be returned instead. This means that you cannot do if (*foo{SCALAR}) instead of if (defined $$foo) because the former will always be true. Also, for subroutines there is a difference between definedness and typeglob slot existance. A forward-declared subroutine will have *foo{CODE} return true, but if it isn't defined with a body later on (sub foo { ... }) defined &foo will return false. (You can also check if a subroutine has been declared (independently of defined) with exists &foo). It's up to you how you choose to handle this. A forward declaration might indicate that the subroutine will be generated or handled by an AUTOLOAD routine, so you could claim that it exists, when needed.

There's more to it that I have explained here. This will hopefully get you started though. Quite frankly, I don't know all the magic that goes on under the hood. I know enough to use them effectively, but what actually happens is not for me to answer. (In fact, if I've gotten something wrong, or something is explained in a weird or backward manner, please notify me one way or another. I'd be nothing but glad if someone would fill in the goriest details or correct my ignorance.)

Futher reading can be found in perldata, perlref, and perlmod.

Hope I've helped,
ihb