Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

I'm in trouble with Perl's OO programming

by cybergeek (Novice)
on Jun 09, 2005 at 08:12 UTC ( #465001=perlquestion: print w/replies, xml ) Need Help??

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

Hi guys, could you help me on that one : I'm using two classes, one derived from the other one. My code looks like this :

* Super class (called "A") contains:
sub Schedule { my ($self) = @_; $self->addTab(\&WatchLog); $self->addTab(\&SendEmail); } sub WatchLog { ... } sub SendEmail { ... } sub addTab { ... } sub go { my ($self) = @_; foreach my $code (@{$self->{CODETAB}}){ &{$code}($self)) } }
* The derived class (called "B") contains :
use base A .... sub Schedule { my ($self) = @_; $self->addTab(\&AddLocalTime); $self->addTab(\&WatchLog);#This method is defined in the paren +t class $self->addTab(\&SendEmail); #Same here } sub AddLocalTime { }

If I create an instance of B in a script and call $b->Schedule I get an "undefined subroutine" error for the two references corresponding to the parent's methods.

I don't know how to get a reference to a parent's method (A) from the derived class (B). One hinted me to use the can method.

More over, I have been told that using references to dynamically call methods was not the best approach. Do you have an idea about how I could do the same code while being more respectful of the object structure ?

My final aim being that the script having the B instance is able to modify the Schedule by removing, inserting, adding method calls into the Scheduler via various addTab,delTab .... methods.


** UPDATED ** The solution which consist in calling the "can" method works !

Replies are listed 'Best First'.
Re: I'm in trouble with Perl's OO programming
by guha (Priest) on Jun 09, 2005 at 09:16 UTC

    Inheritance does not work when you access WatchLog and SendEmail as subroutines. In fact for the sake of symmetry I would use a method invokation for AddLocalTime or at least change the name to _AddLocalTime to indicate that it is a local class method intended for use within the class.

    use base A .... sub Schedule { my ($self) = @_; $self->addTab(_AddLocalTime); $self->addTab($self->WatchLog); $self->addTab($self->SendEmail); } sub _AddLocalTime { }

      That will actually call the methods at the point of definition, not in the loop. I think you meant instead:

      $self->addTab( sub { $self->WatchLog() } );

      Another option is to provide the name of methods to call:

      $self->addTab( 'WatchLog' );

      and then call them with:


      That's really late binding and it can be useful.

        You are right! I thoroughly misunderstood the OP.

Re: I'm in trouble with Perl's OO programming
by revdiablo (Prior) on Jun 09, 2005 at 17:30 UTC
    The solution which consist in calling the "can" method works

    It sounds like you have found a working solution, but in case you still wonder why your original code didn't work (and for the sake of others who don't know), I'll reply to your code as originally posted.

    The main problem I see is that you're trying to take a reference to a method. The thing is, a method is just a plain subroutine that has been called against an object. It doesn't become a method until a certain action is taken. You can't directly take a reference to that action; you can only take a reference to the subroutine.

    When you take a reference to a subroutine, you are taking a reference to that exact subroutine. It is unambiguous. It does not travel the inheritance tree looking for parent packages with subroutines named the same thing. That doesn't even make sense all the time, as subroutine references can be anonymous, in which case they neither belong to a package nor have a name.

    So what you need to do is encapsulate the action in a reference. The way to encapsulate action is with subroutines, so you have to create another subroutine that calls the target as a method. This may sound like a hassle, but Perl makes it pretty simple:

    $self->addTab(sub { $self->WatchLog });

    Now, when you want to execute that reference, you don't need to do any additional work to call it as a method. It already encapsulates its own method-calling behavior, so you can simply execute the coderef:

    foreach my $code (@{$self->{CODETAB}}){ $code->(); }

    This also gives you the flexibility to create code tabs that aren't methods. I'm not sure if that's a flexibility you really want, but it's definitely something I would consider.

    As for the can solution, I imagine you are doing something along the lines of:


    This avoids your problem by traversing the inheritance tree to find the subroutine reference. It still takes a direct reference to the subroutine, but this time it's the correct subroutine, and you can fudge the method-calling semantics as you did before.

    Update: typo, s/corrent/correct/

      Thank you so much for this explanation. It is now clearer to me.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://465001]
Approved by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (7)
As of 2021-03-03 09:07 GMT
Find Nodes?
    Voting Booth?
    My favorite kind of desktop background is:

    Results (76 votes). Check out past polls.