Determine if a base routine has been overridden from a base class

This question put me on my guard, but since then I have solved the problem and will post my answer.

I have a base class Parent

Parent

package Parent;

sub new {
    my $c = shift;
    my $s = {}, bless $s, $c;
    return $s;
}

sub who {
    return "parent";
}

1;

      

This parent class provides one method; who

...

I wrote two child classes: Child

and Freak

. Only Child

overrides who

.

Child

package Child;

use base 'Parent';

sub who {
    return "child";
}

1;

      

Freak

package Freak;

use base 'Parent';

1;

      

How can the base class determine if the base method has been overridden who

?

I want to write something like this

package Parent;

sub new {
    my $c = shift;
    my $s = {}, bless $s, $c;
    return $s;
}

sub who {
    return "parent";
}

sub check {
  my $self = shift;

  my $is_overridden = 1; # what conditional should be here?

  return $is_overridden ? "yes" : "no";
}

1;

      

What conditional value can be used to determine if a subroutine is overridden?

+3


source to share


3 answers


my $is_overridden = $self->can("who") != Parent->can("who");

      

See the documentation for UNIVERSAL

that defines can

.



Also, I should add a more philosophical note - it seems that your override violates the Liskov Subscription Principle , it would probably be better to refactor things so that the method override isn't something you need to investigate elsewhere in the code.

+7


source


Parent->UNIVERSAL::can('who') ne Child->UNIVERSAL::can('who')

      

UNIVERSAL::can(PACKAGE,METHODNAME)

returns a reference to a subroutine that will be called when the name of the given method is used in this package. If the packages are different, but the return values ​​are the same, this means that one package inherits a method from the other (or they both inherit from a common source). This method works for several levels of inheritance:

package Parent;
sub foo { 42 }
sub bar { 19 }
sub baz { 47 }

package Child;
@Child::ISA = qw(Parent);
sub bar { 19 }

package Grandchild;
@Grandchild::ISA = qw(Child);
sub foo { 42 }

##############

package main;

print "foo:", Parent->UNIVERSAL::can('foo'), 
    Child->UNIVERSAL::can('foo'),
    Grandchild->UNIVERSAL::can('foo'),"\n";

print "bar:", Parent->UNIVERSAL::can('bar'), 
    Child->UNIVERSAL::can('bar'),
    Grandchild->UNIVERSAL::can('bar'),"\n";

print "baz:", Parent->UNIVERSAL::can('baz'), 
    Child->UNIVERSAL::can('baz'),
    Grandchild->UNIVERSAL::can('baz'),"\n";

      



Typical output:

foo:CODE(0x17e26e8)CODE(0x17e26e8)CODE(0x17e2b38)
bar:CODE(0x17e27a8)CODE(0x17e29b8)CODE(0x17e29b8)
baz:CODE(0x17e2850)CODE(0x17e2850)CODE(0x17e2850)

      

+4


source


My answer to this dilemma is as follows:

The existence of methods inside packages can be determined by statically checking its CODE reference as follows:

if( defined &Child::who ){
  return "yes";
}

      

But since the parent can be overridden by many, many different children, and I don't want to access all of them manually, I will get the package reference (which will be the package name in string form) and evaluate it.

sub check {
  my $self = shift;
  my $package = ref $self; # 'Freak' or 'Child', etc.

  my $is_overridden = eval "defined &".$package."::who";
  die "eval error occurred" if $@;

  return $is_overridden ? "yes" : "no";
}

      

And there you have it!

0


source







All Articles