Generating Attribute Methods Using AUTOLOAD (Perl Cookbook, 2nd Edition)

## 13.12. Generating Attribute Methods Using AUTOLOAD

### 13.12.1. Problem

Your object needs accessor methods to set or get its data fields, and you're tired of writing them all out one at a time.

### 13.12.2. Solution

Carefully use Perl's AUTOLOAD mechanism as something of a proxy method generator so you don't have to create them all yourself each time you want to add a new data field.

### 13.12.3. Discussion

Perl's AUTOLOAD mechanism intercepts all possible undefined method invocations. To disallow arbitrary data names, we store the list of permitted fields in a hash. The AUTOLOAD method checks for whether the accessed field name is in that hash.

>     package Person;
>     use strict;
>     use Carp;
>     our(%ok_field);

>     # Authorize four attribute fields
>     for my $attr ( qw(name age peers parent) ) { $ok_field{$attr}++; }

>     sub AUTOLOAD {
>         my $self = shift;
>         my $attr = our $AUTOLOAD;
>         $attr =~ s/.*:://;
>         return unless $attr =~ /[^A-Z]/;  # skip DESTROY and all-cap methods
>         croak "invalid attribute method: ->$attr( )" unless $ok_field{$attr};
>         $self->{uc $attr} = shift if @_;
>         return $self->{uc $attr};
>     }
>     sub new {
>         my $proto  = shift;
>         my $class  = ref($proto) || $proto;
>         my $parent = ref($proto) && $proto;
>         my $self = {  };
>         bless($self, $class);
>         $self->parent($parent);
>         return $self;
>     }
>     1;

This class supports a constructor method named `new`, plus four attribute methods: `name`, `age`, `peers`, and `parent`. Use the module this way:

>     use Person;
>     my ($dad, $kid);
>     $dad = Person->new;
>     $dad->name("Jason");
>     $dad->age(23);
>     $kid = $dad->new;
>     $kid->name("Rachel");
>     $kid->age(2);
>     printf "Kid's parent is %s\n", $kid->parent->name;
>     Kid's parent is Jason

This is tricky when producing inheritance trees. Suppose you'd like an Employee class that had every data attribute of the Person class, plus two new ones, like `salary` and `boss`. Class Employee can't rely upon an inherited `Person::AUTOLOAD` to determine what Employee's attribute methods are. So each class would need its own `AUTOLOAD` function. This would check just that class's known attribute fields, but instead of croaking when incorrectly triggered, it would invoke overridden superclass version.

Here's a version that takes this into consideration:

>     sub AUTOLOAD {
>         my $self = shift;
>         my $attr = our $AUTOLOAD;
>         $attr =~ s/.*:://;
>         return if $attr eq "DESTROY";

>         if ($ok_field{$attr}) {
>             $self->{uc $attr} = shift if @_;
>             return $self->{uc $attr};
>         } else {
>             my $superior = "SUPER::$attr";
>             $self->$superior(@_);
>         }
>     }

If the attribute isn't in our OK list, we'll pass it up to our superior, hoping that it can cope with it. But you can't inherit this `AUTOLOAD`; each class has to have its own, because it is unwisely accessing class data directly, not through the object. Even worse, if a class A inherits from two classes B and C, both of which define their own `AUTOLOAD`, an undefined method invoked on A will hit the `AUTOLOAD` in only one of the two parent classes.

We could try to cope with these limitations, but `AUTOLOAD` eventually begins to feel like a kludge piled on a hack piled on a workaround. There are better approaches for the more complex situations.

One further proviso: the `UNIVERSAL::can` method will not normally report as invokable a method that would only trigger a class's AUTOLOAD. If you prefer that it do so, declare the methods without defining them. For example:

>     sub eat;
>     sub drink;
>     sub be_merry;
>     sub AUTOLOAD  {
>         my $self = shift;
>         my $funcname = our $AUTOLOAD;
>         $funcname =~ s/.*:://;
>         ...
>     }

You don't normally need to declare functions to trigger AUTOLOAD. If you had an object of that class:

>     $man->be_merry( );

the AUTOLOAD would still trigger, even without the declarations. However, you need the declarations to make the `can` method notice them:

>     $man->be_merry( ) if $man->can("be_merry");

### 13.12.4. See Also

The examples using AUTOLOAD in perltoot(1); Chapter 10 of Programming Perl; Recipe 10.15

Copyright © 2003 O'Reilly & Associates. All rights reserved.