Recipe 13.11. Generating Attribute Methods Using AUTOLOAD
Author: Tom Christiansen & Nathan Torkington
Date: 1999-07-02T01:42:27Z

#

| Chapter 13 Classes, Objects, and Ties |

## 13.11. Generating Attribute Methods Using AUTOLOAD

### 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.

### Solution

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

### Discussion

Perl's AUTOLOAD mechanism intercepts all possible undefined method calls. So as not to permit arbitrary data names, we'll store the list of permitted fields in a hash. The AUTOLOAD method will check to verify that the accessed field is in that hash.

    package Person;
    use strict;
    use Carp;
    use vars qw($AUTOLOAD %ok_field);

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

    sub AUTOLOAD {
        my $self = shift;
        my $attr = $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 named `new`, and 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 call its overridden superclass version.

Here's a version that takes this into consideration:

    sub AUTOLOAD {
        my $self = shift;
        my $attr = $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 call 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.

### See Also

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

13.10. Accessing Overridden Methods |  | 13.12. Solving the Data Inheritance Problem