http://qs321.pair.com?node_id=8227

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question: (object-oriented programming)

How can I use AUTOLOAD to magically define get and set methods for my member data?

Originally posted as a Categorized Question.

  • Comment on How can I use AUTOLOAD to magically define get and set methods for my member data?

Replies are listed 'Best First'.
Re: How can I use AUTOLOAD to magically define get and set methods for my member data?
by Perlmage (Acolyte) on Apr 21, 2000 at 00:28 UTC

    Here's a slightly more complex version that creates actual methods on the fly so that you avoid the overhead of an AUTOLOAD method call on the second and successive calls.

    It also uses magic GOTO, which is a cool hack in itself.

    #!/usr/bin/perl -w
    package myClass;
    use strict;
    use vars qw{$AUTOLOAD $Debug};
    
    $Debug = 1;
    
    sub new {
    	return bless {
    		thing	=> 1,
    		thang	=> 2,
    		thong	=> 3,
    	}, shift;
    }
    
    sub AUTOLOAD {
    	my $self = shift or return undef;
    
    	# Get the called method name and trim off the fully-qualified part
    	( my $method = $AUTOLOAD ) =~ s{.*::}{};
    
    	# If the data member being accessed exists, build an accessor for it
    	if ( exists $self->{$method} ) {
    
    		### Create a closure that will become the new accessor method
    		my $accessor = sub {
    			my $closureSelf = shift;
    
    			if ( @_ ) {
    				return $closureSelf->{$method} = shift;
    			}
    
    			return $closureSelf->{$method};
    		};
    
    		# Assign the closure to the symbol table at the place where the real
    		# method should be. We need to turn off strict refs, as we'll be mucking
    	        # with the symbol table.
    	  SYMBOL_TABLE_HACQUERY: {
    			no strict qw{refs};
    			*$AUTOLOAD = $accessor;
    		}
    
    		# Turn the call back into a method call by sticking the self-reference
    		# back onto the arglist
    		unshift @_, $self;
    
    		# Jump to the newly-created method with magic goto
    		goto &$AUTOLOAD;
    	}
    
    	### Handle other autoloaded methods or errors
    }
    
    DESTROY {}
    
    
    ### Test program
    package main;
    
    my $a = new myClass;
    
    print $a->thing, $a->thang, $a->thong, "\n";
    
Re: How can I use AUTOLOAD to magically define get and set methods for my member data?
by saucepan (Scribe) on Dec 05, 2000 at 06:48 UTC

    If you do this, you may want to provide a new can() method for people to use:

    # can - Help UNIVERSAL::can() cope with our AUTOLOADed methods sub can { my ($self, $method) = @_; my $subref = $self->SUPER::can($method); return $subref if $subref; # can found it; it's a real method # Method doesn't currently exist; should it, though? return unless exists $self->{$method}; # Return an anon sub that will work when it's eventually called sub { my $self = $_[0]; # The method is being called. The real method may have been # created in the meantime; if so, don't call AUTOLOAD again my $subref = $self->SUPER::can($method); goto &$subref if $subref; $AUTOLOAD=$method; goto &AUTOLOAD }; }

    (This one assumes that people will often call can() without intending to use the method ref it returns, and so it defers calling AUTOLOAD until someone goes to use it. Because of this, it needs a second check to see if someone else has already AUTOLOADed the desired method in the meantime.)

Re: How can I use AUTOLOAD to magically define get and set methods for my member data?
by chromatic (Archbishop) on Apr 20, 2000 at 23:20 UTC
    Here's a quick and dirty autoloader. It assumes your member data is stored in a hash, and that the get and set functions take the form "get_foo" and "set_bar".
    use vars '$AUTOLOAD'; # a while later sub AUTOLOAD { my $self = shift; return if ($AUTOLOAD =~ /DESTROY/); # don't mess with garbage c +ollection if ($AUTOLOAD =~ /.*::set_(\w+)/) and (@_) { return $self->{$1} = shift; } elsif ($AUTOLOAD ~= /.*::get_(\w+)/) { return $self->{$1}; } # handle bad methods here, return undef ? }
Re: How can I use AUTOLOAD to magically define get and set methods for my member data?
by jeteve (Pilgrim) on Apr 01, 2005 at 15:13 UTC
    Ok, for the non french reader, this is the AUTOLOAD method generating code :
    sub AUTOLOAD{ my ($self,$value)= @_ ; return if $AUTOLOAD =~ /::DESTROY$/ ; my $attname = $AUTOLOAD; $attname =~ s/.*::// ; if(! exists $self->{$attname}){ Carp::confess("Attribute $attname does not exists in $self"); } my $pkg = ref($self ) ; my $code = qq{ package $pkg ; sub $attname { my \$self = shift ; \@_ ? \$self->{$attname} = shift : \$self->{$attname} ; } }; eval $code ; if( $@ ){ Carp::confess("Failed to create method $AUTOLOAD : $@"); } goto &$AUTOLOAD ; }
Re: How can I use AUTOLOAD to magically define get and set methods for my member data?
by jeteve (Pilgrim) on Apr 01, 2005 at 15:04 UTC
    There's also a module I wrote that generates the accessor methods on demand at run time. You just have to inherit from and you can get/set all your instance variable automagically. It is dynamic and not time consuming since once the accessor method is generated, it's used as a regular perl method. Take a look at: Nice article and perl code It's in french. Enjoy !!

      Well, my French is very poor, but I think I understood the essential parts, and if there's written what I understood, I like it.

      There are other interesting possibilities, e.g. creating an anonymous subroutine and write it into symbol table, e.g.

      my $code = sub { # for a better way, see Tanktalus' post below my ($self, $value) = @_; $self->{$attname} = $value if defined $value; return $self->{$attname}; }; no strict 'refs'; *{$attname} = $code; # put it into symbol table use strict 'refs'; return $self->$code(@params); # or: goto &$AUTOLOAD;
      But I prefer - if possible - pregenerating the object interface methods at the startup of the module and not using AUTOLOAD, because I think AUTOLOAD is cool, but not using AUTOLOAD is cooler ;-) e.g.
      my @attributes = qw(color speed); # generate the methods foreach my $attribute (@attributes) { my $code = sub { # for a better way, see Tanktalus' post below my ($self, $value) = @_; $self->{$attribute} = $value if defined $value; return $self->{$attribute}; }; no strict 'refs'; *{ $attribute } = $code; } # foreach

      Best regards,
      perl -e "s>>*F>e=>y)\*martinF)stronat)=>print,print v8.8.8.32.11.32"

        This is where getting perl to do it in perl 6 is going to be cleaner...

        $obj->color(undef);
        Does this set the color to undef? Perhaps you mean:
        my $code = sub { my $self = shift; $self->{$attribute} = shift if @_; $self->{$attribute}; }
        (Falling off rather than returning is just a tiny bit faster according to the benchmarks I've done in the past.) Here, if you pass anything in, it'll get set, but we won't set to undef if you don't pass anything in. Your original is ok if undef is the sole invalid value. That is actually more rare than some people may think - in true perl fashion, by allowing anything, you leave your code more flexible. The primary exceptions are generally external standards, and, even then, should only be when you're outputting them, not interpreting them on input. That is, be conservative in what you send (stick to the letter of the standard), but liberal in what you receive (allow variations on the input). For get/set methods, I find it extremely rare that I don't want the ability to set a variable to undef to signify its invalid or unknown state. For example, setting color to undef simply means that, at this point in the program, I don't know the color, even if I thought I did earlier.

        Yep, this have the advantage to avoid AUTOLOAD. But consider the following point: If you put my AUTOLOAD method in a module called AutoAccess, you just have to write something like
        package Foo ; use base qw/AutoAccess/ ; sub new{ bless { 'a1' => undef , 'a2' =>undef } , shift ; } 1;
        To have the benefits of the auto accessors. The methods 'a1' and 'a2' are implemented 'on demand' to use the nowadays buzzword :) If you'd like to change the implementation and want to keep the behaviour of the class from the user code point of view, you just have to implement by hand the concerned accessor methods. Anyway, I also think that explicitely write which attribute should be concerned by auto accessor generation can be a good thing. Specialy to write code that don't look too magic to new readers :) Is there a way to use the 'my @attributes = qw(color speed);' method in a factorized way ?