Hack 75. Find All Symbols in a Package


Explore symbol tables without soft references.

One of the earliest temptations for novice programmers is to use the contents of one variable as part of the name of another variable. After making one too many costly mistakes or showing such code to a more experienced programmer, novices start to use the strict pragma to warn them about dubious constructs.

However, several advanced features of Perl, such as the implementation of the Exporter module, are only possible by reading from and writing to the symbol table at run time. Normally strict forbids thisbut it's possible to access global symbols at run time with strict enabled.

This is an easy way to find out if a symbolsuch as a scalar, array, hash, subroutine, or filehandleexists.

The Hack

Suppose you want to check whether a specific type of variable is present in a given namespace. You need to know the name of the package, the name of the variable, and the type of the variable.

Defining the following subroutine in the UNIVERSAL package makes the class method contains_symbol available to any package:[4]

[4] Er...class.

my %types = (     '$' => 'SCALAR',     '@' => 'ARRAY',     '%' => 'HASH',     '*' => 'IO',     '&' => 'CODE', ); sub UNIVERSAL::contains_symbol {     my ($namespace, $symbol) = @_;     my @keys                 = split( /::/, $namespace );     my $type                 = $types{ substr( $symbol, 0, 1, '' ) }                             || 'SCALAR';     my $table = \\%main::;     for my $key (@keys)     {         $key .= '::';         return 0 unless exists $table->{$key};         $table = $table->{$key};     }     return 0 unless exists $table->{$symbol};     return *{ $table->{$symbol} }{ $type } ? 1 : 0; }

To see if a symbol exists, for example to test that contains_symbol exists in the UNIVERSAL package, call the method like:

print "Found it!\\n" if UNIVERSAL->contains_symbol( '&contains_symbol' );

How does it work?

Perl uses the same data structure for hashes as it does for symbol tables. The same operationsstoring and retrieving values by key; iterating over keys, values, or both; and checking the existence of a keywork on both. The secret is knowing how to access the symbol table.

The main symbol table is always available as the hash named %main::. Every other symbol table has an entry starting there. For example, strict's symbols are available in $main::{'strict::'}, while CGI::Application's symbols are in $main::{'CGI::'}{'Application::'}. Each level is a new hash reference.

The quotes are important to identify the name with the colons appropriately.


Within a symbol table, all leaf entries (values that aren't themselves symbol tables) contain typeglobs. A typeglob is similar to a hash, but it cannot contain arbitrary keysit has a fixed set of keys, as shown in the %types array.[5]

[5] There are other keys, but they're less common and rarely worth mentioning.

Because a typeglob isn't a hash, you can't access its members as you would a hash. Instead, you must dereference it with the leading * glob identifier, then subscript it with the name of the slot to check.

Running the Hack

Once you have the glob, you can assign references to it to fill in its slots. For example, to create a new subroutine growl( ) in Games::ScaryHouse::Monster, find the symbol table for Games::ScaryHouse::Monster and the glob named growl. Then assign a reference to a subroutine to the glob and you will be able to call it as Games::ScaryHouse::Monster::growl( ). Similar techniques work for anything else to which you can take a reference.

This trick offers some benefits over other ways of querying for a symbol, such as using soft references, calling can( ) on subroutines (which may run afoul of inheritance), and wrapping potentially harmful accesses in eval blocks. However do note that an optimization[6] automatically created a scalar entry in every new glob. Thus if you have a package global hash named %compatriots, contains_symbol( ) will claim that $compatriots also exists.

[6] Removed as of Perl 5.9.3.

This technique does not work on lexical variables; they don't live in symbol tables!




Perl Hacks
Perl Hacks: Tips & Tools for Programming, Debugging, and Surviving
ISBN: 0596526741
EAN: 2147483647
Year: 2004
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net