Cratylus
Cratylus

Reputation: 54074

How can I pass a module's function as a reference to another module in Perl?

How can I pass a reference to a module's function as parameter in a function call of another module?
I tried the following (simple example):
This is the module that has a function (process_staff) that takes as a parameter a function reference (is_ok).

#!/usr/bin/perl  
use strict;  
use warnings;  

package Objs::Processing;  

sub new {  
    my ($class) = @_;  
    bless {} ;      
}  

sub process_staff {  
    my ($employee, $func) = @_;  
    if($func->is_ok($employee)) {  
        print "Is ok to process\n";  
    }  
    else {  
        print "Not ok to process\n";  
    }  
}  
1; 

This is the module that implements the passed function (is_ok)

#!usr/bin/perl  
use strict;  
use warnings;  

package Objs::Employee;  

my $started;  

sub new {  
    my ($class) = @_;  
    my $cur_time = localtime;  
    my $self = {  
        started => $cur_time,  
    };  
    print "Time: $cur_time \n";  
    bless $self;  
}  

sub get_started {  
    my ($class) = @_;  
    return $class->{started};  
}  

sub set_started {  
    my ($class, $value) = @_;  
    $class->{started} = $value;  
}  

sub is_ok {  
    my ($emp) = @_;  
    print "In is ok I received:\n";  
    use Data::Dumper;   
    print Dumper($emp);  
    return 1;  
}   

This is my test script that I run:

#!/usr/bin/perl  
use strict;  
use warnings;  

use Objs::Manager;  
use Objs::Processing;  

my $emp = Objs::Manager->new('John Smith');    
use Data::Dumper;    
print Dumper($emp);  

my $processor = Objs::Processing->new();   
$processor->process_staff(\&$emp->is_ok);   #error is here  

I get a:

Not a CODE reference at testScript.pl line 14.  

I also tried: $processor->process_staff(\&$emp->is_ok()); but also still does not work.
What am I doing wrong here

Upvotes: 5

Views: 1323

Answers (5)

ikegami
ikegami

Reputation: 385657

It's not specifically what you asked for, but I think you simply need the following:

sub process_staff {  
    my ($self, $emp) = @_;  
    if ($emp->is_ok()) {  
        print "Is ok to process\n";  
    }  
    else {  
        print "Not ok to process\n";  
    }  
}  

$processor->process_staff($emp);

Upvotes: 1

mpapec
mpapec

Reputation: 50637

You can pass anonymous coderef which returns result from desired method,

$processor->process_staff(sub{ $emp->is_ok(@_) });

@_ can be dropped as is_ok method doesn't take any arguments.

Upvotes: 2

ysth
ysth

Reputation: 98388

You appear to want to pass an object and a method to call on it; the easiest way to do that would be:

$processor->process_staff( sub { $emp->is_ok } );

where process_staff looks like:

sub process_staff {
    my ($self, $func) = @_;
    if ( $func->() ) {
        ...

or you can pass the reference and the object separately:

sub process_staff {
    my ($self, $emp, $method) = @_;
    if ( $emp->$method() ) {
    ...

$processor->process_staff( $emp, $emp->can('is_ok') );

Upvotes: 5

friedo
friedo

Reputation: 66957

In this expression:

$processor->process_staff(\&$emp->is_ok); 

You are saying "call the method $emp->is_ok, take the return value, treat it as a CODE reference, dereference it, and return a reference to that. That doesn't work, since the return value from that sub is not a CODE reference.

To do what you want, you can use a reference to an anonymous sub to wrap the call to your object method:

$processor->process_staff( sub { $emp->is_ok } );  

Upvotes: 2

Neil Slater
Neil Slater

Reputation: 27207

I think this could work with:

$processor->process_staff(\&Objs::Employee::is_ok);

where you pass in the method ref.

and where you currently have

if( $func->is_ok($employee) ) { 

you need

if( $func->( $employee ) ) { 

This is because you cannot reference named methods simply from an object, by the syntax \&$obj->method.

However, in your example code it is not at all clear why you don't do this instead:

if( $employee->is_ok() ) { 

in which case you would not need to reference the method to call in process_staff at all. There are also other ways to achieve the same method indirection that might give you better encapsulation in future.

Upvotes: 1

Related Questions