Yet another OO Meditation...

One of my most and least favorite features of OO in Perl is that method calls are dynamic. Dynamic method calls means you can write code like this:

... my $recs = $dbh->selectall_arrayref( $sql ); my @people; foreach my $rec ( @$recs ) { my $i = 0; my $person = Person->new(); foreach my $meth ( qw( name hight weight age gender dob ) ) { $person->$meth( $rec->[$i++] ); } push @people, $person; }
The other edge of the sword means that you can call methods that don't exist and Perl can't catch them untill run-time. Which is annoying and can be a pain to track down in your code. One technique that helps ease that pain is to define an AUTOLOAD method in the base class to tell you when you've called a method that doesn't exist (possibly due to a typo) as opposed to other run-time errors (such as trying to call a method on an undefined value).
package Base; use strict; use warnings; our @ISA = qw(); use vars qw( $AUTOLOAD ); sub new { ... } sub throw { my $self = shift; my($p,$f,$l) = caller(); die "Exception at: ",$p,"->$f($l)\n ",join('',@_); } # report non-existant methods sub AUTOLOAD { my($self) = @_; $self->throw("Error, unsupported method: $AUTOLOAD\n"); } 1;
With an AUTOLOAD in the base class, code that calls methods that don't exist, like:
$person->shoeSize();
Will now throw an exception that tells you right where (package, file and line number) the offending code tried to call the non-existant method. This technique has helped reduce the time I spend debugging.

Replies are listed 'Best First'.
Re: YAOOM
by dragonchild (Archbishop) on Dec 07, 2001 at 21:19 UTC
    Instead of using AUTOLOAD, why not just use can()?
    my $recs = $dbh->selectall_arrayref( $sql ); my @people; foreach my $rec ( @$recs ) { my $i = 0; my $person = Person->new(); foreach my $meth ( map { $person->can($meth) } qw( name hight weig +ht age gender dob ) ) { $person->$meth( $rec->[$i++] ) if $meth; } push @people, $person; }

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Instead of using AUTOLOAD, why not just use can()?

      Because AUTOLOAD leaves the test in the package, whereas can requires the test everywhere the package is used in the application?

        p

      Just to be picky: you either want a grep instead of a map or you need to rewrite the foreach since can() returns a coderef:

      foreach my $meth ( grep { $person->can( $_ ) } ...

      Chris
      M-x auto-bs-mode

        The code, as written, works just fine. can() will either return a coderef or undef. Hence, the if $meth check. I used map to allow for warnings, if desired. If no warnings are desired, grep is the better way to go.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Actualy, the technique is less for actual dynamic method calling, and more for actual method typos in the code.
Re (tilly) 1: YAOOM
by tilly (Archbishop) on Dec 08, 2001 at 03:27 UTC
    I am wondering what you are trying to do:
    tilly@prod1:~$ perl -e 'Foo->hello' Can't locate object method "hello" via package "Foo" at -e line 1.
    It seems to me that I already get the package, line number and file name of the error in the error message. With your code I get a different format, and I get the package, line number, and file name of the AUTOLOAD, which obscures the original message.

    Now if you had gone out of your way to throw a confess (from Carp) to give a stack backtrace, I would find the extra output sometimes useful. But it is counterproductive as it stands.