4

Given a base class and a list of classes derived from it:

package base
{
    # ...
}

package foo
{
    our @ISA = 'base';
    # ...
}

package bar
{
    our @ISA = 'base';
    # ...
}

Is there a runtime way to get a list of classes which have base as parent?

I know I could easily work around this by adding their names to a list manually, but I was wondering if base itself could tell me who inherited from it.

2 Answers 2

6

Since Perl 5.10, Perl has come with a module called mro which includes a whole bunch of functions for inspecting class hierarchies.

You can find child classes of My::Class using:

use mro;

my $base_class = 'My::Class';
print "$_\n" for @{ mro::get_isarev( $base_class ) };

The mro documentation includes various caveats, such as the fact that calling it on the 'UNIVERSAL' package doesn't work properly. There will be other cases it copes badly with, but if you're "doing normal stuff", it should work.

0

If you don't know the names of all the "potential" classes, you can recursively iterate through the complete "namespace".

sub inspect {
    my ($package, $search_for) = @_;
    my @result;
    my $keys = 'sort keys (%' . $package . '::)';
    $package=~ s/main:://;
    my @keys = eval $keys;
    foreach my $lookup (@keys) {
        $lookup =~ s/main:://;
        if ($lookup =~ /(.*)::$/) {
            push @result, inspect($package.'::'.$1, $search_for);
        }
    }
    push @result, $package
         if $package->isa($search_for);
    return @result;
}

so in your example:

print "Number of derived classes: " . (inspect('main', 'base') -1) .  "\n";

we have to extract one, as the class is an instance of its own.

AFAIK base doesn't store the "class-tree" anywhere.

8
  • Re "AFAIK base doesn't store the "class-tree" anywhere.", This is not stored in the base module, no. (And if you're referring to base.pm -- which isn't used here -- then it doesn't either)
    – ikegami
    Commented Dec 7, 2022 at 14:51
  • This suffers from a code injection bug. But it's very unlikely to cause harm, intentionally or otherwise. (Packages with very weird names can be created.) I actually upvoted despite this, though I did post an alternate approach of doing the same thing which doesn't suffer from the code injection bug (and which is clearer in my opinion).
    – ikegami
    Commented Dec 7, 2022 at 22:23
  • @ikegami You mean the my $keys = 'sort keys (%' . $package . '::)'; my @keys = eval $keys;. One could, of course, check $package, but I tried to write that code without an eval and didn't find a way to. Do you have an idea? Commented Dec 8, 2022 at 9:42
  • You'd use %:: or my $pkg_ref = do { no strict 'refs'; \%{ $package . '::' } }; %$pkg_ref. My answer used %::, but I deleted it because the now-accepted answer is better.
    – ikegami
    Commented Dec 8, 2022 at 13:31
  • Thx for pointing this out. Using no strict 'refs'; in a block to avoid using eval is a suitable solution here. %:: is an alias for %main:: AFAIK. perl -e 'print \%:: eq \%main::' prints out '1' Commented Dec 12, 2022 at 13:48

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.