Pass scalar/list context to called subroutine
- by Will
I'm trying to write a sub that takes a coderef parameter. My sub does some initialization, calls the coderef, then does some cleanup.
I need to call the coderef using the same context (scalar, list, void context) that my sub was called in. The only way I can think of is something like this:
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;
}
}
Obviously there's a good deal of redundancy in this code. Is there any way to reduce or eliminate any of this redundancy?
EDIT I later realized that I need to run $code->() in an eval block so that the cleanup runs even if the code dies. Adding eval support, and combining the suggestions of user502515 and cjm, here's what I've come up with.
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->()) });
}
This gets rid of the redundancy, though unfortunately now the control flow is a little harder to follow.