Mailing List Archive

New AutoLoader for discussion
I've finally got around (tuit, actually I stole one) and I've taken
another look at how the AutoLoader interface works.

The basic problem was the tendency for other modules to say
@ISA = qw(... AutoLoader ...)
and thus expose the AutoLoader's specialised import method to
being inherited by other modules that have not been AutoSplit.

AutoLoading is a class implementation issue and should not be inherited.

I've reworked the AutoLoader.pm and made the corresponding minor
changes to the other modules which use it. Rather than throw out
a large patch I thought I mail just the new AutoLoader.pm for
discussion first.

I think I've added enough documentation for it to be self explanetory.

Comments? (Soon please since I don't want to hold Andy up.
I plan to post the full patch late Monday afternoon GMT).

Tim.

p.s. Things would have been fractionally cleaner if 'use Foo ();' was
different to 'use Foo;'. Oh well.


package AutoLoader;
use Carp;

=head1 NAME

AutoLoader - load functions only on demand

=head1 SYNOPSIS

package FOOBAR;
use AutoLoader 'AUTOLOAD'; # import the default AUTOLOAD function

package FOOBAR;
use AutoLoader; # don't import AUTOLOAD, define our own
sub AUTOLOAD {
...
$AutoLoader::AUTOLOAD = "...";
goto &AutoLoader::AUTOLOAD;
}

=head1 DESCRIPTION

The AutoLoader module works with the AutoSplit module and the __END__
token to defer the loading of some functions until they are used rather
than loading them all at once.

The author of a module has to place the definitions of functions to be
AutoLoaded after an __END__ token. See L<perldata>. The AutoSplit module
can then be run manually to extract the definitions and split them into
individual files F<auto/$AUTOLOAD.al>. See L<perlsub/"Autoloading">.

The AutoLoader module implements an AUTOLOAD function that will read
and evaluate the F<auto/$AUTOLOAD.al> which corresponds to the function
being called. It will then C<goto> the newly defined function.

Since the function is now defined future calls to the function will
bypass the AUTOLOAD mechanism.

=head2 Function Stubs

In order for object method lookup and/or prototype checking to operate
correctly even when methods have not yet been defined it is necessary
to at least "forward declare" each function (as in C<sub NAME;>).
See L<perlsub/"SYNOPSIS">.

Fortunately the AutoSplit and AutoLoader modules automate this.
The AutoSplit module creates an 'index' file containing forward
declarations of all the AutoSplit functions. When the AutoLoader module
is 'use'd it loads the declarations into its callers package.

Because of this it is important that the AutoLoader module is always
'use'd and not 'require'd.

=head2 The Default AUTOLOAD Function

In order to use the AutoLoader's AUTOLOAD function it I<must> be
explicitly imported:

use AutoLoader 'AUTOLOAD';

=head2 Overriding The AUTOLOAD Function

Some modules, mainly extensions, override the default AUTOLOAD function
provided by the AutoLoader. They typically need to check for some
special cases (such as constants) and then fallback to the AutoLoader's
AUTOLOAD function for the rest.

In this case the AutoLoader's AUTOLOAD function should I<not> be
explicitly imported and the module must define it's own AUTOLOAD
function along these lines:

use AutoLoader;

sub AUTOLOAD {
my $constname;
($constname = $AUTOLOAD) =~ s/.*:://;
my $val = constant($constname, @_ ? $_[0] : 0);
if ($! != 0) {
if ($! =~ /Invalid/) {
$AutoLoader::AUTOLOAD = $AUTOLOAD;
goto &AutoLoader::AUTOLOAD;
}
else {
croak "Your vendor has not defined constant $constname";
}
}
eval "sub $AUTOLOAD { $val }";
goto &$AUTOLOAD;
}

If a module's own AUTOLOAD function has no need to fallback to the
AutoLoader's AUTOLOAD function (because it doesn't have any AutoSplit
functions) then the AutoLoader module should not be used at all.

=head2 WARNING

AutoLoader versions prior to 5.002 had a slightly different interface.
Any old modules which use the AutoLoader should be changed to the new
calling style.

Typically this just means changing a require to a use, adding the
explicit 'AUTOLOAD' import if needed and removing AutoLoader from
@ISA.

=head1 SEE ALSO

SelfLoader - an autoloader that doesn't use external files.

=cut


AUTOLOAD {
my $name = "auto/$AUTOLOAD.al";
$name =~ s#::#/#g;
eval { require $name };
if ($@) {
# The load might just have failed because the filename was too
# long for some old SVR3 systems which treat long names as errors.
# If we can succesfully truncate a long name then it's worth a go.
# There is a slight risk that we could pick up the wrong file here
# but autosplit should have warned about that when splitting.
if ($name =~ s/(\w{12,})\.al$/substr($1,0,11).".al"/e){
eval { require $name };
}
elsif ($AUTOLOAD =~ /::DESTROY$/) {
eval "sub $AUTOLOAD {}";
}
if ($@){
$@ =~ s/ at .*\n//;
croak $@;
}
}
goto &$AUTOLOAD;
}


sub import {
my($class, @symbols) = @_;
my $callclass = caller(0);

confess "Can't export @symbols"
if ((@symbols and $symbols[0] ne "AUTOLOAD") or @symbols>1);

# First do a normal export of the AUTOLOAD function if required
*{"${callclass}::AUTOLOAD"} = \&AUTOLOAD if $symbols[0] eq "AUTOLOAD";

load_stubs($callclass);
}


sub load_stubs {
my($callclass) = @_;

# Then try to find our callers autosplit index file. Eg., if the call
# package is POSIX, then $INC{POSIX.pm} is something like
# '/usr/local/lib/perl5/POSIX.pm', and the autosplit index file is in
# '/usr/local/lib/perl5/auto/POSIX/autosplit.ix', so we require that.
#
# However, if @INC is a relative path, this might not work. If,
# for example, @INC = ('lib'), then
# $INC{POSIX.pm} is 'lib/POSIX.pm', and we want to require
# 'auto/POSIX/autosplit.ix' (without the leading 'lib').

my($callpack, $path);
($callpack = $callclass) =~ s#::#/#;
if (defined($path = $INC{"$callpack.pm"})) {
# Try absolute path name first.
$path =~ s#^(.*)$callpack\.pm$#$1auto/$callpack/autosplit.ix#;
eval { require $path; };
# If that failed, try relative path with normal @INC searching.
if ($@) {
$path ="auto/$callpack/autosplit.ix";
eval { require $path; };
}
carp $@ if $@;
} else {
# XXX one day we might want to be more clever here
confess "Can't AutoLoader::load_stubs for $callclass - no pm file!";
}
}


1;