Reputation: 1683
I've been playing with AUTOLOAD
to create my accessors in Perl and I have encountered this confusion (I have searched google and perldoc already).
I have this code:
package Class;
sub new {
..code for constructor here.
}
sub AUTOLOAD {
my $name= shift;
print $name;
}
But when I do something like : my $a=Class->new;
The autoload subroutine still executes, and prints Class=HASH(some weird number)
;
I thought AUTOLOAD only runs when there is an undefined method or subroutine?
And also I did this:
my $class = our $AUTOLOAD;
print $class #prints ::DESTROY
Am I right when I assumed that DESTROY
is the value of $AUTOLOAD when no undefined function is passed?
Upvotes: 2
Views: 1481
Reputation: 57590
Using Autoload is inherently difficult. If you want a solid object system that makes accessors for you then please use Moose, Mouse, Moo, or just loop over your fields and install the accessors yourself:
BEGIN {
my @fields = qw/foo bar baz/;
for my $field (@fields) {
no strict 'refs';
# install a closure in the package stash.
*{ __PACKAGE__ . "::" . $field } = sub {
my $self = shift;
$self->{$field} = shift if @_;
return $self->{$field};
};
}
}
If a class that can AUTOLOAD
encounters an undefined method, the AUTOLOAD
sub is called with the arguments of the missing sub. The fully qualified name of the requested sub is passed in the $AUTOLOAD
package variable.
A typical Autoload sub would look like:
use Carp;
my %fields_allowed = map {$_ => 1} qw/foo bar baz/;
sub AUTOLOAD {
my $field = our $AUTOLOAD;
$field =~ s/.*:://; # strip the package name
$fields_allowed{$field}
or croak qq(Can't locate object method $field via package "@{[__PACKAGE__]}");
my $self = shift;
$self->{$field} = shift if @_;
return $self->{$field};
}
There remain two problems:
DESTROY
method is called on the object if it provides one. We can prevent autoloading of DESTROY
by providing an empty implementation: sub DESTROY {}
.say "Good dog" if $dog->can("roll")
. Therefore, we have to override can
to support our autoloading. The can
method is useful for safe duck typing. Every object inherits from UNIVERSAL
, which provides default implementations for can
and isa
.The contract of can
is that it takes the name of a method. It will return undef
when the object cannot perform the method, or a code reference to that method if it can. A suitable implementation would be
sub can {
my ($self, $name) = @_;
# check if it's a field of ours
if ($fields_allowed{$name}) {
return sub {
my $self = shift;
$self->{$name} = shift if @_;
return $self->{$name};
};
}
# Ask SUPER implementation of can if we can do $name
if (my $meth = $self->SUPER::can($name)) {
return $meth;
}
return; # no method found
}
We can now simplify AUTOLOAD
to
sub AUTOLOAD {
my $field = our $AUTOLOAD;
$field =~ s/.*:://; # strip the package name
my $code = $self->can($field)
or croak qq(Can't locate object method $field via package "@{[__PACKAGE__]}");
goto &$code; # tail call; invisible via `caller()`.
}
This is a lot of complexity to get right. Verdict: Don't use Autoload because you think it might be less work. It never is. It is quite useful for implementing a proxy pattern, but that is a bit advanced.
I urge you to dabble around with OO basics, and the Moose object system, before diving deep into Perl's unique and strange features.
Upvotes: 8