package SUPERB;
use Carp;
sub AUTOLOAD {
my $self = shift;
my $caller = caller;
$AUTOLOAD =~ s/.*:://;
for my $method ($AUTOLOAD, "AUTOLOAD") {
my @queue = @{"$caller\::ISA"};
while (@queue) {
my $class = shift @queue;
if ( *{"$class\::$method"}{CODE} ) {
my $call = "$class\::$AUTOLOAD";
return $self->$call(@_);
} else {
push @queue, @{"$class\::ISA"};
}
}
}
my $class = ref $self || $self;
croak qq[Can't locate object method "$AUTOLOAD" via package "$class"];
}
1;
####
use SUPERB;
package Base;
sub foo { print "Base::foo\n"; }
sub bar { print "Base::bar\n"; }
sub baz { print "Base::baz\n"; }
package A; @ISA = qw/Base/;
sub foo { print "A::foo\n"; }
package B; @ISA = qw/Base/;
sub bar { print "B::bar\n"; }
package C; @ISA = qw/A B/;
sub foo { print "C::foo\n"; shift->SUPERB::foo; }
sub bar { print "C::bar\n"; shift->SUPERB::bar; }
sub baz { print "C::baz\n"; shift->SUPERB::baz; }
package main;
my $c = bless [], 'C';
$c->foo;
$c->bar;
$c->baz;
__OUTPUT__
C::foo
A::foo
C::bar
B::bar
C::baz
Base::baz
##
##
package bfs_dispatch;
use Carp;
sub import {
my ($x, @isa) = @_;
my $caller = caller;
*{"$caller\::AUTOLOAD"} = sub {
my $self = shift;
$AUTOLOAD =~ s/.*:://;
for my $method ($AUTOLOAD, "AUTOLOAD") {
my @queue = @isa;
while (@queue) {
my $class = shift @queue;
if ( my $code = *{"$class\::$method"}{CODE} ) {
my $call = "$class\::$AUTOLOAD";
return $self->$call(@_);
} else {
push @queue, @{"$class\::ISA"};
}
}
}
my $class = ref $self || $self;
croak qq[Can't locate object method "$AUTOLOAD" via package "$class"];
};
}
1;
##
##
## replace this:
package C; @ISA = qw/A B/;
sub foo { print "C::foo\n"; shift->SUPERB::foo; }
sub bar { print "C::bar\n"; shift->SUPERB::bar; }
sub baz { print "C::baz\n"; shift->SUPERB::baz; }
## with this:
package C;
use bfs_dispatch qw/A B/;
## methods deleted, lets see if the method dispatcher finds the correct one!
##
##
__OUTPUT__
A::foo
B::bar
Base::baz
##
##
package C;
@ISA = qw/A B/;
sub foo { $_[0]->SUPERB::foo(@_[1..$#_]); }
sub bar { $_[0]->SUPERB::bar(@_[1..$#_]); }
sub baz { $_[0]->SUPERB::baz(@_[1..$#_]); }