${...}
- это скалярный оператор разыменования, а не анонимный конструктор подпрограмм. Вы хотите:
my $coderef = sub {MyModule::MyTool->new};
И если ваш конструктор принимает аргументы, вы можете написать это так:
my $coderef = sub {MyModule::MyTool->new(@_)};
Два приведенных выше примера не решают одну проблему, которая сохраняет функциональность caller
. Если ваш конструктор нуждается в этом (многие этого не делают), вы можете использовать магический синтаксис Perl goto &sub
:
my $coderef = sub {unshift @_, 'MyModule::MyTool'; goto &{$_[0]->can('new')} };
Это, вероятно, требует небольшого объяснения. Во-первых, имя модуля помещается перед любыми другими аргументами (это то, что ожидает метод new
). Затем я использовал UNIVERSAL
метод ->can
, чтобы получить coderef для метода new
. goto &{...}
затем переходит на этот код, используя текущий список аргументов.
РЕДАКТИРОВАТЬ: комментарии ниже показывают, что есть некоторая путаница относительно того, когда вам нужно будет использовать более длинный третий метод. Вот короткий сегмент, который показывает проблему:
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
Короче говоря, подпрограмма Encapsulate::Better
сохраняет точную функциональность Original->new
, тогда как Encapsulate::Simple
навсегда связывает ее с пакетом, нарушая любые инкапсулированные методы, которые используют caller
для чего-либо.