Home > OS >  Can I pass the code reference of an object's method in Perl?
Can I pass the code reference of an object's method in Perl?

Time:04-22

In a network handler dealing with various parameters to get and set I'm using closures heavily. I have a subroutine that receives a closure and builds another closure using that passed as parameter on return (Sounds complicated, but that's just why I want such). Now I have a case where I would have to pass two very similar closures, each using the same object method, but with different parameters (the object method checks the number of parameters passed).

My idea was not to pass two (or more) similar closures, but pass a reference $meth_ref to the object's method (the object is also passed to the function returning closures), so that the function can use the code reference to pass varying parameters.

Unfortunately I didn't find out the syntax to do so.

Code sketch:

sub closure_maker($$)
{
    my ($obj, $meth_ref) = @_;

    return sub (...) {
        $meth_ref->($obj);
        ...
        $meth_ref->($obj, ...);
    };
}

my @handlers = (closure_maker($obj1, ???), closure_maker($obj2, ???));

I hope you get the idea.

CodePudding user response:

Use $obj->$method_name().

sub closure_maker($$)
{
    my ($obj, $method_name) = @_;

    return sub (...) {
        $obj->$method_name();
        ...
        $obj->$method_name(...);
    };
}

my @handlers = map { closure_maker($_, "method_name") } $obj1, $obj2;

CodePudding user response:

You can get a reference to a method with \&Classname::method that can then be used with a given object of that class. For example:

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;

package Example {
    sub new {
        my ($class, $name) = @_;
        return bless { name => $name }, $class;
    }

    sub method1 {
        my $self = shift;
        say "Method 1 of $self->{name}: @_";
    }
};

sub make_closure($$) {
    my ($obj, $method) = @_;
    return sub { $obj->$method(@_); }
}

my $obj1 = Example->new("Bob");
my $obj2 = Example->new("Cindy");
my $closure1 = make_closure($obj1, \&Example::method1);
my $closure2 = make_closure($obj2, \&Example::method1);
$closure1->(qw/1 2/);
$closure2->(qw/A B C/);

outputs

Method 1 of Bob: 1 2
Method 1 of Cindy: A B C

If you're paranoid, you might want to add type checking to the methods to make sure they're not accidentally called with an object of the wrong class. The isa operator, added in perl 5.32, makes this easy:

# In package Example
use Carp;
sub method1 {
    use experimental qw/isa/;
    my $self = shift;
    croak "Invalid object; should be an Example" unless $self isa Example;
    say "Method 1 of $self->{name}: @_";
}

Older versions can use the built-in isa method:

# In package Example
use Carp;
use Scalar::Util qw/blessed/;
sub method1 {
    my $self = shift;
    croak "Invalid object; should be an Example"
        unless defined blessed $self && $self->isa("Example");
    say "Method 1 of $self->{name}: @_";
}

CodePudding user response:

I found a solution that is really ugly, but it works (in the Debugger at least). The sample session is from the real code:

### this is the sample method:
  DB<5> x MessageLogFormat::log_appname($log_format)
0  ''
  DB<11> $xxxx = \&{*{MessageLogFormat::log_appname}}

  DB<12> x ref $xxxx
0  'CODE'
  DB<13> x $xxxx->($log_format)
0  ''
  DB<14> x $xxxx->($log_format, 1)
0  1
### NAME() just outputs the name of the class
  DB<17> $n = $log_format->NAME()
  DB<19> $xxx = \&{*{${n}.'::log_appname'}}
### the method is actually a closure (provided by class `Class`) by itself
  DB<20> x $xxx
0  CODE(0x1af8960)
   -> &Class::__ANON__[lib/Class.pm:85] in lib/Class.pm:78-85
  DB<21> x $xxx->($log_format)
0  1
  DB<22> x $xxx->($log_format, 0)
0  ''
  DB<23> x $xxx->($log_format)
0  ''
### So that worked; try a non-closure method, too:
  DB<24> $xxx = \&{*{${n}.'::new'}}
  DB<25> x $xxx
0  CODE(0x1afcb88)
   -> &MessageLogFormat::new in lib/MessageLogFormat.pm:85-91

In the solution sketched I don't have to know (and write down explicitly) the class of each object being used.

  • Related