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

How can I make an AUTOLOAD sub declaration in a child class not hide the AUTOLOAD sub of parent?

I use AUTOLOAD functions to automagically generate sub routines for accessing data from my objects. However, if a class which inherits from my class declares an AUTOLOAD subroutine then they will hide my AUTOLOAD.

Is there a way to do this? Am I terribly confused?

Here is some code to illustrate the problem:
Class B inherits from class A. Class A uses AUTOLOAD to generate a baz() method. Class B uses AUTOLOAD to generate a foo() function.

The Problem:
Class B's AUTOLOAD routine captures the baz() call, and I can't figure out how to pass that to Class A's AUTOLOAD function.

package A; sub bar { return "BAR"; } sub AUTOLOAD { my (@objpath) = split(/::/, $AUTOLOAD); my $fname = pop @objpath; return if $fname eq 'DESTROY'; if ( $fname eq 'baz' ) { my $subr = <<EOSUB; sub $AUTOLOAD { return "FOO"; } EOSUB eval $subr; goto &$AUTOLOAD; } return; } package B; @ISA = qw( A ); sub new { return bless {}, shift; } sub AUTOLOAD { my (@objpath) = split(/::/, $AUTOLOAD); my $fname = pop @objpath; return if $fname eq 'DESTROY'; if ( $fname eq 'foo' ) { my $subr = <<EOSUB; sub $AUTOLOAD { return "FOO"; } EOSUB eval $subr; goto &$AUTOLOAD; } return; } package main; my $b = new B; print "foo: ", $b->foo, "\n"; print "bar: ", $b->bar, "\n"; print "baz: ", $b->baz, "\n";

Replies are listed 'Best First'.
Other Options
by chromatic (Archbishop) on Mar 30, 2000 at 08:03 UTC
    • Use a BEGIN block in the super class -- have AUTOLOAD create the functions right after the class is compiled.
    • Use a closure to generate your subs. Something like:
      sub gen_sub { my $self = shift; my $type = shift; my $sub_ref = sub { if (defined $self{$type}) { $value = $self{$type}; return $value; } else { return undef; } } *{"get_$type"} = $sub_ref; }
      Actually, looking at it now, that would be good for an AUTOLOAD. Do something like:
      sub AUTOLOAD { no strict "refs"; my ($self, $value) = @_; # alias these to make it easier later if ($AUTOLOAD =~ /::get_(\w+)/) { *{$AUTOLOAD} = sub { return $_[0]->{$1} }; return $self->{$1}; } elsif ($AUTOLOAD =~ /::set_(\w+)/){ *{$AUTOLOAD} = sub { $_[0]->{$1} = $_[1] }; $self->[$1] = $value; return; } # handle other methods here or throw a warning }
    • Tell people not to override AUTOLOAD without a good reason. If they have a good reason, figure out how to call your AUTOLOAD themselves. :)
    • Some other method... use $self->can($functionname) to see if an object has some method.
    • (good bits of code stolen from Object Oriented Perl, bad bits from me.)
Re: AUTOLOAD functions & inheritence
by chromatic (Archbishop) on Mar 30, 2000 at 01:51 UTC
    Kinda messy. I know there's a better way to do it, but I don't have the reference book with me. Here's what I came up with:
    package A; sub bar { return "BAR"; } sub AUTOLOAD { my (@objpath) = split(/::/, $AUTOLOAD); my $fname = pop @objpath; return if $fname eq 'DESTROY'; if ( $fname eq 'baz' ) { my $subr = <<EOSUB; sub $AUTOLOAD { return "FOO"; } EOSUB eval $subr; goto &$AUTOLOAD; return $subr; } return; } package B; @ISA = qw( A ); sub new { return bless {}, shift; } sub AUTOLOAD { my (@objpath) = split(/::/, $AUTOLOAD); my $fname = pop @objpath; return if $fname eq 'DESTROY'; if ( $fname eq 'foo' ) { my $subr = <<EOSUB; sub $AUTOLOAD { return "FOO"; } EOSUB eval $subr; goto &$AUTOLOAD; } else { return &{"A::$fname"}; } return; } package main; my $b = new B; print "foo: ", $b->foo, "\n"; print "bar: ", $b->bar, "\n"; print "baz: ", $b->baz, "\n";
    The biggest change I made was adding an else clause to B::AUTOLOAD(). If the appropriate subroutine isn't found in package B, it constructs a symbolic reference to a function in A. Since that function may not be there, it's trapped by AUTOLOAD.

    I have no idea whether or not this gets caching, but I'll check. It's not very clean or elegant or safe, so I wouldn't use it except in an emergency. Still, it's One Way To Do It.

      I tried to do a simmilar thing with SUPER::.
      if ( handle this ) { eval "sub $AUTOLOAD {}"; goto &$AUTOLOAD } else { goto &{"SUPER::$fname"}; }
      I didn't want to presume which parent function to call. -Sean.
        I tried to do a simmilar thing with SUPER::.

        if ( handle this ) { eval "sub $AUTOLOAD {...}"; goto &$AUTOLOAD } else { goto &{"SUPER::$fname"}; }

        I didn't want to presume which parent function to call.


         
        -Sean.

Re: AUTOLOAD functions & inheritence
by btrott (Parson) on Mar 30, 2000 at 02:12 UTC
    This is just an example, right, that you've given us? Because I'm just wondering why you have the statements like
    if ($fname eq 'foo') { ... }
    etc. You're not going to put in statements like that for every possible method that you're trying to trap in the AUTOLOAD, are you? Because that kind of defeats the purpose of having an AUTOLOAD method--if you're just testing for specific methods, why not define them in the first place?

    This has a point, because my contention is that the example you've given us isn't all that helpful. AUTOLOAD should trap any methods that you don't explicitly handle in your code; so in package B, how do you know when you should pass a certain method on to A's AUTOLOAD? You're not just planning on explicitly testing for all of those cases in B, are you?

    I'd like to get a better idea of what you're trying to do, because it's possible that you're asking an XYZ question. In other words, perhaps there's another way to do what you're doing, but I don't know what it could be, because I don't know what you're trying to do.

      It is an XYZ question. I want to know if it is possible to make things happen this specific way. This isn't a question to solve a specific problem. It is a question about the functionality of PERL. Thanks, for caring enough to post :). -Sean.

        In reply to myself (I didn't realize I was anonymous), I think I could give you a less abstract description of the specific problem that inspired this question about perl AUTOLOAD behavior.

        I created a class called AutoType that would autoload accessor methods. AutoType is meant to be a base class. The method names/data names were stored in an array. The code if ($fname -eq 'foo') was actually if (grep {$fname -eq $_} @attribs).

        I was afraid that if someone used AutoType but then created a AUTOLOAD function of their own, AutoType's AUTOLOAD function would never be used and AutoType wouldn't function.

        What I could do instead would be to test for the functions' existence and eval $func_decl the function if it doens't already exist.

        -Sean.