nysus has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I'm going through Conway's OO book. I put together his example code from Chapt. 3 and up to section 3.3.3 Automating data member access on page 91:

#!c:/perl/bin/perl -w package CD::Music; use strict; use vars '$AUTOLOAD'; sub AUTOLOAD { my ($self) = @_; $AUTOLOAD =~ /.*::get(_\w+)/ or die "No such method: $AUTOLOAD"; exists $self->{$1} or die "No such attribute: $1"; return $self->{$1} } { my $_count = 0; sub get_count { $_count } my $_incr_count = sub { ++$_count }; sub new { my ($class) = @_; $_incr_count->(); bless { _name => $_[1], _artist => $_[2], _publisher => $_[3], _ISBN => $_[4], _tracks => $_[5], _room => $_[6], _shelf => $_[7], _rating => $_[8], }, $class; } sub get_location { ($_[0]->{_room}, $_[0]->{_shelf}) } sub set_location { my ($self, $shelf, $room) = @_; $self->{_room} = $room if $room; $self->{_shelf} = $shelf if $shelf; return ($self->{_room}, $self->{_shelf}); } sub set_rating { my ($self, $rating) = @_; $self->{_rating} = $rating if defined $rating; return $self->{_rating}; } } package main; my $cd = CD::Music->new( "Canon in D", "Pachelbel", "Boering Muß GmbH" +, "1729-67836847-1", 1, 8, 8, 5.0); print $cd->get_name, ", ", $cd->get_publisher, "\n"; printf "Room %s, shelf %s\n", $cd->get_location; $cd->set_location(5,3); print CD::Music->get_count, "\n";

I'm getting some strange behavior that I can't explain. I stepped through the program with the debugger and discovered that after the program executes line 19:
sub get_count        { $_count },
the program jumps up and to the 'AUTOLOAD' subroutine which generates the following error:
(in cleanup) No such method: CD::Music::DESTROY at d_autoload line 10.
So where is this DESTROY method coming from and why is the programming executing the AUTOLOAD subroutine after line 19 anyway???

Thanks!

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot";
$nysus = $PM . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Strange code execution with 'AUTOLOAD'
by Masem (Monsignor) on Jul 01, 2001 at 22:24 UTC
    Having run into this yesterday, if you defined AUTOLOAD, you are required to define a DESTROY method in your class (in otherwords, you're getting that error as run-time as the class tries to fully define itself, include DESTROY, and fails). DESTROY is the destructor, and if you do any special resource allocation (opened files, database connections) this is the point where you want to dispose of them. However, if you are only holding data in your class and nothing else, DESTROY can simply be an empty method.

    Define this in your class, and you'll find that your class should work now.


    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      Mucho thanks. I'm guessing this is new to 5.6.1, then? It's not mentioned in the Conway book (at least not what I've read so far).

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot";
      $nysus = $PM . $MCF;
      Click here if you love Perl Monks

Re: Strange code execution with 'AUTOLOAD'
by btrott (Parson) on Jul 01, 2001 at 22:32 UTC
    DESTROY is the destructor for the class. If you use AUTOLOAD but not DESTROY, you will get the above error message, because AUTOLOAD is called for the DESTROY method (because AUTOLOAD catches all undefined method calls on the object), and you do not have a DESTROY method to call.

    Since you have Object Oriented Perl, I suggest you look at page 112, at the section "Destructors and autoloading". It explains the situation far better than I can, and offers a solution (actually, two).

    For those who don't have the book :), the best solution is to define an empty DESTROY method in your class:

    sub DESTROY { }
    The existence of this method will prevent AUTOLOAD from being called on object destruction.
      Another option is to set your autoload like this:
      sub AUTOLOAD { my ($self) = @_; # don't do any work if we are being called for DESTROY next if(substr($AUTOLOAD, -7) eq 'DESTROY'); $AUTOLOAD =~ /.*::get(_\w+)/ or die "No such method: $AUTOLOAD"; exists $self->{$1} or die "No such attribute: $1"; return $self->{$1} }
      Notice how it returns if this is a call to destroy? AUTOLOAD will never be called if there is a DESTROY method. I imagine sub DESTROY {} is faster, but the logic seems easier to maintain and less people will be asking "Why does my autoload work in this class and not that one?"
        Yes, this is the second option presented in OOP that I mentioned. :)

        However, as Damian Conway writes (and as you note), calling AUTOLOAD when you mean DESTROY, and when you *know* what you want to do with DESTROY, is less efficient than just defining an empty DESTROY method with

        sub DESTROY { }
        As for ease of maintenance, I would make the case that it is just as easy to maintain an empty method stub as a special case in AUTOLOAD, if not easier. And it is certainly clearer when looking at the code: rather than destruction behavior being buried in the definition of an AUTOLOAD, you have a defined DESTROY method to show that there is, in effect, no special destruction code.