Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 348
Cratylus Avatar asked Feb 15 '23 03:02

Cratylus


2 Answers

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') );
like image 108
ysth Avatar answered Apr 28 '23 03:04

ysth


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.

like image 20
Neil Slater Avatar answered Apr 28 '23 04:04

Neil Slater