开发者

How to take code reference to constructor?

开发者 https://www.devze.com 2023-01-25 20:02 出处:网络
I have following code: my $coderef = ${MyModule::MyTool->开发者_运维技巧new}; but when I try $coderef->();

I have following code:

my $coderef = ${MyModule::MyTool->开发者_运维技巧new};

but when I try

$coderef->();

i got error:

Not a CODE reference

How can I take reference to constructor (without calling it) and run referenced code late?


The ${...} is the scalar dereference operator, not the anonymous subroutine constructor. You want:

my $coderef = sub {MyModule::MyTool->new};

And if your constructor takes arguments, you could write it this way:

my $coderef = sub {MyModule::MyTool->new(@_)};

The two examples above do not address one issue, and that is preserving the functionality of caller. If your constructor needs this (many do not), you can use Perl's magic goto &sub syntax:

my $coderef = sub {unshift @_, 'MyModule::MyTool'; goto &{$_[0]->can('new')} };

That probably requires a little explanation. First, the module name is placed before any other arguments (which is what the new method will expect). Then I used the UNIVERSAL method ->can to retrieve the coderef for the new method. goto &{...} then jumps to that coderef using the current argument list.

EDIT: The comments below show that there is some confusion as to when you would need to use the longer third technique. Here is a short segment that shows the problem:

package Original;
    sub new {say +(caller)[0]}  # presumably some better use of caller
                                # or Carp (which uses caller)

package Encapsulate::Simple;
    sub new {
        my (undef, $invocant, $method) = @_;
        sub {$invocant->$method(@_)}
    }

package Encapsulate::Better;
    sub new {
        my (undef, $invocant, $method) = @_;
        sub {unshift @_, $invocant; goto &{$invocant->can($method)}}
    }

package main;

my $bad = Encapsulate::Simple->new(qw/Original new/);

$bad->(); # always prints 'Encapsulate::Simple'

my $good = Encapsulate::Better->new(qw/Original new/);

$good->(); # prints 'main' as it should

package another;

$bad->();  # erroneously prints 'Encapsulate::Simple' again
$good->(); # prints 'another' as it should

So in short, Encapsulate::Better's sub preserves the exact functionality of Original->new whereas Encapsulate::Simple permanently binds it to its package, breaking any encapsulated methods that use caller for anything.


Use \& to obtain a reference to a named function:

my $coderef = \&MyModule::MyTool::new;


This should work regardless of which package holds the new:

my $coderef = MyModule::MyTool->UNIVERSAL::can( 'new' ); 

So that if MyModule::MyTool does not implement their constructor, you can still get it's handle.

0

精彩评论

暂无评论...
验证码 换一张
取 消