Pass scalar/list context to called subroutine

Posted by Will on Stack Overflow See other posts from Stack Overflow or by Will
Published on 2010-11-28T00:07:37Z Indexed on 2011/01/03 5:53 UTC
Read the original article Hit count: 239

Filed under:

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.

© Stack Overflow or respective owner

Related posts about perl