views:

137

answers:

2

In Perl, it's pretty trivial to specify a callback or code reference if its package is known:

package Foo;

sub foo { print "in foo" }

# and then
package main;

sub baz {
    my $code = shift;
    $code->();
}

baz( \&Foo::foo );

And this prints in foo.

Lets say you have an object, ever so trivial, like this:

package Foo;

sub new { bless {}, shift }
sub bar { print "in bar" }
sub baz { print "in baz" }

You can look up the method using the above way (\&Package:Method) and call it like

package main;
my $foo = Foo->new();
my $ref = \&Foo::bar;
$foo->$ref();

But sometimes (okay, often) you don't know the explicit type. Lets say there's Foo, Bar, Baz, and they all have their own blat method. You'd want to get the reference to the appropriate method, based on the object instead of the package. How would you go about that?

+3  A: 

Let method lookup do the work for you:

$ cat try
#! /usr/bin/perl

use warnings;
use strict;

package Foo;
sub new { bless {} => shift }
sub blat { "Foo blat" }

package Bar;
sub new { bless {} => shift }
sub blat { "Bar blat" }

package Baz;
sub new { bless {} => shift }
sub blat { "Baz blat" }

package main;

my $method = "blat";
foreach my $obj (Foo->new, Bar->new, Baz->new) {
  print $obj->$method, "\n";
}

$ ./try
Foo blat
Bar blat
Baz blat

If you need a reference, keep in mind that Perl doesn't have delegates, but you can get close:

my @objs = (Foo->new, Bar->new, Baz->new);

my $method = "blat";
my $obj = $objs[rand @objs];
my $ref = $obj->can($method);

if ($ref) {
  print $ref->($obj), "\n";
}
else {
  print "$obj: no can $method\n";
}

Even closer would be:

my $delegate = sub { $obj->$ref };
#           or sub { $obj->$method }
print $delegate->(), "\n";
Greg Bacon
can is defiantly the right way to go - but the UNIVERSAL perldoc page explicitly says DO NOT do UNIVERSAL::can() directly - this makes it so overrides don't work. see http://perldoc.perl.org/UNIVERSAL.html
Robert P
Fair enough. Revised.
Greg Bacon
+6  A: 
my $ref = $obj->can('blat');

If $ref is undef, your object can't blat. If $ref is not undef, it's a valid CODE reference to the function in question, suitable for calling "$obj->$ref(@args)".

Tanktalus
Perfect. Thanks!
Robert P