Belmark Caday
Belmark Caday

Reputation: 1683

What is the default parameter for AUTOLOAD in Perl?

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

Answers (1)

amon
amon

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:

  • When the reference count of an object drops to zero, or when a thread terminates, the DESTROY method is called on the object if it provides one. We can prevent autoloading of DESTROY by providing an empty implementation: sub DESTROY {}.
  • We can ask any object if it can perform a certain method, like 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

Related Questions