Professional Perl | 10
[previous] |
Professional Perl
Checking for Subroutines and Defining Subroutines
On-the-fly
We can check for the existence of a subroutine before we call it using Perl's defined function:
if (defined &capitalize) {
capitalize(@countries);
}
This is more useful than it might seem. For instance, when using a library that may or may not support a particular subroutine (depending on the installed version) we can safeguard against a possible exit from our program by checking that the library has the function before we try to call it.
If we are writing object-oriented Perl, we can use the special object method can (supplied by the UNIVERSAL object  that's a subject for Chapter 18 though), in order to do the same thing in a more object-oriented style:
$bean->jump('left') if $bean->can('jump');
We are not limited to just testing for the existence of subroutines. We can also substitute for them and even define them on the fly by defining an AUTOLOAD subroutine. If an AUTOLOAD subroutine exists in the same package as a non-existent subroutine, Perl will call it, rather than exiting with an error. The name of the missing subroutine, complete with package name, is placed in the special package variable $AUTOLOAD, and the arguments passed to the subroutine are instead passed to AUTOLOAD. As a trivial example, the following AUTOLOAD subroutine just returns the missing subroutine name as a string:
sub AUTOLOAD {
our $AUTOLOAD; # or 'use vars' for Perl
Because $AUTOLOAD is a package variable which we have not declared, we need to gain access to it with the our directive if use strict is in effect (Perl versions before 5.6 need to have use vars instead). The example above allows us to write strange looking statements like this:
$, = " ";
print "", Hello, Autoloading, World;
This is identical in effect to:
print "main::Hello", "main::Autoloading", "main::World";
In other words, this AUTOLOAD subroutine interprets unqualified barewords as strings. A slightly more useful example of the same technique is shown by this HTML tag generator, which automatically creates matching start and end tags, with any supplied parameters sandwiched in between. Note the regular expression to strip off the package prefix:
sub AUTOLOAD {
our ($AUTOLOAD); # again, 'use vars' if Perl \n". join("\n",@_). "$AUTOLOAD> \n";
}
We can now write an HTML page programmatically using functions that we haven't actually defined, in a similar (and much shorter, albeit less sophisticated) way to the CGI module. Here is an example HTML document created using the above autoloader subroutine in a single line of code:
print html(head(title("Autoloaded HTML")), body(h1("Hi There")));
While functional, this example has a few deficiencies. For a start, we can invent any tag we like, including mis-spelt ones. Another problem is that it does not learn from the past, each time we call a non-existent subroutine, Perl looks for it, fails to find it, then calls AUTOLOAD. It would be more elegant to define the subroutine so that next time it is called, Perl finds it. The chances are that if we use it once, we'll use it again. To do that, we just need to create a suitable anonymous subroutine and assign it to a typeglob with the same name as the missing function, which inserts the new subroutine into the symbol table for us. Here is a modified version that does this for us:
sub AUTOLOAD {
our ($AUTOLOAD);
no strict 'refs';
my $tag = $AUTOLOAD;
$tag =~s/.*:://;
*$AUTOLOAD = sub {
" \n". join("\n", @_). "$tag> \n";
};
&$AUTOLOAD; # we can use a 'goto' here too -- see below
}
Now, whenever a tag is asked for, a subroutine for that tag is defined. The next time the same tag is asked for, the newly defined subroutine catches the call and handles it.
Aside from the anonymous subroutine definition, the other interesting point about this autoloading subroutine is the call to the new subroutine at the end.
Since AUTOLOAD has to define the subroutine the first time it is called, it has to call it as well. We make use of the &subname; syntax to pass the contents of @_ directly to the new subroutine. However, $AUTOLOAD is a symbolic reference, so we use no strict refs at the top of the subroutine.
AUTOLOAD subroutines that define subroutines are one place where using goto does make sense. We can replace the last line of this subroutine with:
goto &$AUTOLOAD;
Why is this useful? Because it removes the AUTOLOAD subroutine itself from the calling stack, so caller will not see the AUTOLOAD subroutine, but rather the original caller. So goto is consequently a common sight in AUTOLOAD subroutines that define subroutines on the fly.
Autoloading is quite handy in functional programming, but much more useful in modules and packages. Accordingly we cover it in more depth in Chapter
[previous] |
Revised: March 13, 2000
Created: March 13, 2000