Я пытаюсь написать подпрограмму, которая принимает параметр coderef. Моя подпрограмма выполняет некоторую инициализацию, вызывает coderef, затем выполняет некоторую очистку.
Мне нужно вызвать coderef, используя тот же контекст (скаляр, список, пустой контекст), в который был вызван мой sub. Единственный способ, которым я могу думать, это что-то вроде этого:
sub perform {
my ($self, $code) = @_;
# do some initialization...
my @ret;
my $ret;
if (not defined wantarray) {
$code->();
} elsif (wantarray) {
@ret = $code->();
} else {
$ret = $code->();
}
# do some cleanup...
if (not defined wantarray) {
return;
} elsif (wantarray) {
return @ret;
} else {
return $ret;
}
}
Очевидно, что в этом коде много избыточности. Есть ли способ уменьшить или устранить любую из этих избыточностей?
EDIT Позже я понял, что мне нужно запустить $code->()
в блоке eval
, чтобы очистка выполнялась, даже если код умирает. Добавление поддержки eval и объединение предложений user502515 и cjm, вот что я придумал.
sub perform {
my ($self, $code) = @_;
# do some initialization...
my $w = wantarray;
return sub {
my $error = $@;
# do some cleanup...
die $error if $error; # propagate exception
return $w ? @_ : $_[0];
}->(eval { $w ? $code->() : scalar($code->()) });
}
Это избавляет от избыточности, хотя, к сожалению, теперь контролировать поток управления немного сложнее.