Checking for Subroutines and Defining Subroutines On the Fly - Page 8
July 13, 2001
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
19 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 < 5.6
return $AUTOLOAD;
}
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 < 5.6
$AUTOLOAD =~ s/^.*:://; # strip the package name
return "<$AUTOLOAD> \n". join("\n",@_).
"</$AUTOLOAD> \n";
}
[Lines 4 and 5 above are one line. They have been split for
formatting purposes.]
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-spelled 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 {
"<$tag> \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
10.
Recursion - Page 7
Professional Perl Programming
Passing Parameters - Page 9
|