Pass scalar / list context to the called routine

I am trying to write a sub that takes a coderef parameter. My unit does some initialization, calls coderef, then does some cleanup.

I need to call coderef using the same context (scalar, list, void context) that my sub was called into. 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 is a lot of redundancy in this code. Is there a way to reduce or eliminate any of these redundancies?

EDIT Later I realized that I needed to run $code->()in a block evalso that the cleanup would be done even if the code dies. Adding support for eval and combining the user502515 and cjm sentences is what I came 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->()) });
}

, , , .

+3
3

!defined wantarray , ( $code->() , , ). , .

-, . - :

sub perform
{
    my($self, $code) = @_;
    if (!defined(wantarray)) {
            $code->();
            return;
    }
    return wantarray ? &cleanup($code->()) : &cleanup(scalar($code->()));
}
+1

:: CPAN. , , (, , ).

+3

, :

sub perform {
    my ($self, $code) = @_;

    # do some initialization...

    my @ret;
    if (not defined wantarray) {
        $code->();
    } else {
        @ret = wantarray ? $code->() : scalar $code->();
    }

    # do some cleanup...

    return wantarray ? @ret : $ret[0];
}

You still have two checks wantarray, but then you need a function cleanupto correctly return the values โ€‹โ€‹(to) to which it was passed. You do not need to worry about undefthe second check, because in this case it does not matter what returns perform.

+1
source

Source: https://habr.com/ru/post/1777243/


All Articles