Invoking Methods Indirectly (Perl Cookbook, 2nd Edition)

## 13.8. Invoking Methods Indirectly

### 13.8.1. Problem

You want to invoke a method by a name that isn't known until runtime.

### 13.8.2. Solution

Store the method name as a string in a scalar variable and use it where you would use the real method name to the right of the arrow operator:

>     $methname = "flicker";
>     $obj->$methname(10);         # invokes $ob->flicker(10);

>     # invoke three methods on the object, by name
>     foreach $m ( qw(start run stop) ) {
>         $obj->$m( );
>     }

### 13.8.3. Discussion

Sometimes you need to invoke a method whose name you've stored somewhere. You can't take the address of a method, but you can store its name. If you have a scalar variable `$meth` containing the method name, invoke the method on an object `$crystal` with `$crystal->$meth( )`.

>     @methods = qw(name rank serno);
>     %his_info = map { $_ => $ob->$_( ) } @methods;

>     # same as this:

>     %his_info = (
>         'name'  => $ob->name( ),
>         'rank'  => $ob->rank( ),
>         'serno' => $ob->serno( ),
>     );

If you're desperate to devise a way to get a method's address, you should try to rethink your algorithm. For example, instead of incorrectly taking `\$ob->method( )`, which simply applies the backslash to that method's return value or values, do this:

>     my $fnref = sub { $ob->method(@_) };

Now when it's time to invoke that indirectly, you would use:

>     $fnref->(10, "fred");

and have the closure in turn correctly use the original value of `$ob` (provided `$ob` was a lexical variable) that was around when it was created to call:

>     $ob->method(10, "fred");

This works even if `$ob` has gone out of scope. This solution is much cleaner.

When using indirect method invocation, it is permitted to store a subroutine reference in the scalar variable instead of a string representing the method name. No verification that the function represents a valid method.

The code reference returned by the UNIVERSAL `can` method should probably not be used for indirect method invocation on objects other than the one on which it was called, or at least those of the same class. That's because you have no reason to believe that this will be a valid method when applied to an object of an arbitrary class.

For example, this is highly dubious code:

>     $coderef = $some_object->can("wither");
>     $other_object->$coderef( );   # wither( ) it even if we shouldn't

That is reasonable only when the two objects are of the same, or compatible, classes. If they were not, and the second did not have a `wither` method, no exception would be raised, unlike here:

>     $some_object->wither( );
>     $other_object->wither( );

Another interesting possibility is to use the strategy outlined in Recipe 12.5 to implement nominally private methods.

>     my $secret_meth = sub { ... }
>     sub reg_meth {
>         my $self = shift;
>         # ... do whatever you want, then
>         $self->$secret_meth(@_);
>         #
>     }

Because the lexical variable `$secret_meth` is scoped to the class module's file, code from outside the class cannot access it, and therefore cannot invoke the closure. However, code that is in the module file can see that scalar, so it can use the code reference with `$secret_meth` to make an indirect method invocation.

When you use a code reference to invoke a method indirectly, Perl doesn't consult a package or its `@ISA` at all; it just makes the function call and passes in the invocant in the initial slot. That means these two lines are the same:

>     $self->$secret_meth(@_);        # indirect method invocation
>     $secret_meth->($self, @_)       # indirect function call

So if you hadn't shifted the invocant off, but had left it in `@_`, then you could just make the equivalent dereferenced function call yourself:

>     sub reg_meth {
>         # ... do whatever you want, then
>         $secret_meth->(@_);
>     }

### 13.8.4. See Also

perlobj(1); Recipe 11.8

Copyright © 2003 O'Reilly & Associates. All rights reserved.