IntrOOspection: ISO an object's methods set

J

J Krugman

I am aware of the UNIVERSAL::can method to determine whether an
object can perform a particular method, but is there a way to get
all the methods that an object can perform (including the inherited
ones, of course)? I realize this question is complicated by
AUTOLOAD, but I'd be happy if I could find all of an object's
non-AUTOLOADed methods.

More generally, how does UNIVERSAL determine whether object $foo
can 'fandango'? Does it simply run eval { $foo->fandango() } and
then pick through the bloody aftermath in $@, or does it use a more
civilized approach, such as looking up some suitable tables? If
the latter is true, how can a program access these tables of methods
directly?

Thanks!

-Jill
 
B

Benjamin Goldberg

J said:
I am aware of the UNIVERSAL::can method to determine whether an
object can perform a particular method, but is there a way to get
all the methods that an object can perform (including the inherited
ones, of course)? I realize this question is complicated by
AUTOLOAD, but I'd be happy if I could find all of an object's
non-AUTOLOADed methods.

Well, would go something like this:

sub list_all_possible_methods {
my $class = ref $_[0] || $_[0];
my $subs = $_[1] || {};
# stash == symbol table hash.
my $stash = do { no strict 'refs'; \%{ $class . "::" } };
while( my ($name, $glob) = each %$stash ) {
next if $subs->{$name};
if( "GLOB" ne ref \$glob ) {
# if it's not really a glob object, then
# we have a sub prototype, but have neither
# a sub body, nor any package variable, nor
# a filehandle associated with this name.
# This is probably due to autoload.ix, produced
# by AutoSplitter.pm during install. Pretend
# that the sub exists.
$subs->{$name} = 1 if UNIVERSAL::can($class, "AUTOLOAD");
} elsif( defined *{$glob}{CODE} ) {
# if a CODE slot exists in the glob, then there's
# a method here!
$subs->{$name} = 1;
}
}
my $isa = $stash->{"ISA"};
return keys %$subs unless defined $isa and "GLOB" eq ref \$isa;
$isa = *{$isa}{ARRAY};
return keys %$subs unless $isa;
foreach my $parent (@$isa) {
list_all_possible_subs($parent, $subs);
}
return keys %$subs;
}

[untested]
More generally, how does UNIVERSAL determine whether object $foo
can 'fandango'? Does it simply run eval { $foo->fandango() } and
then pick through the bloody aftermath in $@, or does it use a more
civilized approach, such as looking up some suitable tables? If
the latter is true, how can a program access these tables of methods
directly?

It uses a more civilized approach, something like:

sub my_can {
my $class = ref $_[0] || $_[0];
my $method = $_[1];
my $namespace = $class . "::";
my $stash = do { no strict 'refs'; \%$namespace };
if( my $glob = $stash->{$method} ) {
if( "GLOB" eq ref \$glob ) {
return *{$glob}{CODE} if defined *{$glob}{CODE};
} else {
no strict 'refs';
return \&{ $namespace . $method };
}
}
my $isa = $stash->{"ISA"};
return unless $isa and "GLOB" eq ref \$isa;
$isa = *{$isa}{ARRAY};
return unless $isa;
foreach my $parent (@$isa) {
my $coderef = my_can( $parent, $method );
return $coderef if defined $coderef;
}
return;
}

[untested]

Except that caching is done to make it faster.

By the way... how do you expect that when you do $foo->fandango, perl
finds fandango in the first place, and why do you think it wouldn't be
able to re-use that code for $foo->can("fandango")?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,262
Messages
2,571,056
Members
48,769
Latest member
Clifft

Latest Threads

Top