Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Dynamic Method Calls

by ton (Friar)
on Aug 28, 2001 at 01:17 UTC ( [id://108271]=perlquestion: print w/replies, xml ) Need Help??

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

Fellow Monks,

Some background first (skip next two paragraphs if you don't care):

I have a class (Foo) that reads in an XML-style configuration file and makes the information available to the caller via a bunch of AUTOLOADed methods. This worked fine for a while, but then the amount of data within Foo got larger, and the number of people maintaining Foo also increased, and it became unmanagable to keep all the parsing and accessing within the same class. I therefore rewrote Foo as a container, with various block objects to parse and store the different sections of the config. In other words, Foo is now little more than an array of block objects with an enumerator, and the block objects do the real work. So far, so good.

I now need to take the AUTOLOADed methods that Foo catches and call them on the current block object. So if a caller does something like $myFoo->Bar(), I need to call the Bar method of the current block. I should mention that the blocks exist within a class hierarchy, so the methods that the class implements might not be in the class package. Ick.

The Problem: I need to call dynamically determined methods on an object, where the method name is contained in a string. As an additional complication, the method might not be in the class receiving the call; it can be in one of its superclasses. An example might help:

Say I have the following class in MyClass.pm:

package MyClass; use strict; sub new($) { my $invocant = shift; my $class = ref($invocant) || $invocant; # object or class name my $self = { }; bless($self, $class); return $self; } sub HelloWorld($) { my $self = shift; print "Hello, World!\n"; } 1;
and I have code calling the class like this:
use MyClass; use strict; my $pack = MyClass->new(); my $string = "HelloWorld"; $string = ref($pack) . "::$string"; no strict 'refs'; &$string($pack);
This works fine. Now suppose that I have a subclass of MyClass named MySubClass (in MySubClass.pm), which looks like this:
package MySubClass; use strict; our @ISA = qw(MyClass); require MyClass; # All implementation is done by the superclass 1;
and I change the test code to use MySubClass instead of MyClass:
use MySubClass; use strict; my $pack = MySubClass->new(); my $string = "HelloWorld"; $string = ref($pack) . "::$string"; no strict 'refs'; &$string($pack);
This will fail with the message "Undefined subroutine &MySubClass::HelloWorld called at test.pl line 9". It appears that perl is looking for HelloWorld within MySubClass.pm, and won't look in packages specified in the @ISA array. Incedentally, calls like "$pack->HelloWorld()" work fine.

So my question is the following: Is there some way I can get method behavior (looking though the namespaces in the @ISA package) when I don't know the name of the method until runtime? Or do I have to look through the @ISA array myself?

Any help would be appreciated,

-Ton
-----
Be bloody, bold, and resolute; laugh to scorn
The power of man...

Replies are listed 'Best First'.
Re: Dynamic Method Calls
by dragonchild (Archbishop) on Aug 28, 2001 at 01:19 UTC
    Uhh... why not just do something like:
    $pack->$string if $pack->can($string);
    That will pass strict 'refs' and will only call the function if the function can be called by that object.

    ------
    /me wants to be the brightest bulb in the chandelier!

    Vote paco for President!

      It may be a matter of taste, but I would prefer to work in a system where a missing method generated a big fat visible error rather than silently not be called. Else I would predict that you will spend a lot of time chasing down little typos that cause method names to be just a tad off from what you intended...
      You can avoid one method lookup by going:
      if (my $code = $pack->can($string)) { &$code($pack, $string); );
        Method lookups get cached, so this isn't really needed. You're actually duplicating the caching efforts that the interpreter does for you.

        ------
        /me wants to be the brightest bulb in the chandelier!

        Vote paco for President!

      That worked perfectly. Thank you!
      -----
      Be bloody, bold, and resolute; laugh to scorn
      The power of man...
Re: Dynamic Method Calls
by runrig (Abbot) on Aug 28, 2001 at 01:23 UTC
    So my question is the following: Is there some way I can get method behavior (looking though the namespaces in the @ISA package) when I don't know the name of the method until runtime? Or do I have to look through the @ISA array myself?

    That's what AUTOLOAD is for. Check out perldoc perltoot.

      err... yes. The reason I have the method as a string in the first place is that I am implementing an AUTOLOAD. I needed to know how to turn that string into a method call on an object; fortunately, dragonchild came through with one line of code. Props to him or her!
      -----
      Be bloody, bold, and resolute; laugh to scorn
      The power of man...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://108271]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2024-04-19 12:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found