How to take code reference to constructor?

前端 未结 3 468
日久生厌
日久生厌 2021-01-20 18:55

I have following code:

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

but when I try

$coderef->();

i got error:

Not a CODE re         


        
3条回答
  •  -上瘾入骨i
    2021-01-20 19:33

    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.

提交回复
热议问题