Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can this perl wrapper function be extended to work with any input function?

Tags:

perl

eval

Consider the following wrapper function that retrys a given function some given number of times if the function throws (not sure why the formatting is wonky):


sub tryit{
    my $fun = shift;
    my $times = shift;
    my @args = @_;
    my $ret;
    do{  
    $times--;
    eval{
        $ret = $fun->(@args);
    };
    if($@){
        print "Error attemping cmd: $@\n";
    }
    else{
         return $ret;
    }
    }while($times > 0);
    return;

}

How can this be extended so that the return value of the parameter function is properly propigated up no matter what kind of value is returned? For instance, this function won't pass an array up properly. You can't just return $fun->() because the return only takes you out of the eval block.

like image 538
frankc Avatar asked Mar 19 '26 05:03

frankc


2 Answers

Same basic answer as Nemo, but with some improvements:

  • Safer exception handling.
  • The exception of the last try isn't caught.
  • Error sent to STDERR.
  • Extra newline removed.
  • Cleaner loop.
  • Better variable names.

wantarray will get you the information you need.

sub tryit {
    my $func        = shift;
    my $attempts    = shift;
    my $list_wanted = wantarray;
    my @rv;
    for (2..$attempts) {
        if (eval{
            if ($list_wanted) {
                @rv = $func->(@_);
            } else {
                $rv[0] = $func->(@_);
            }
            1  # No exception
        }) {
            return $list_wanted ? @rv : $rv[0];
        }

        warn($@, "Retrying...\n");
    }

    return $func->(@_);
}

Void context gets propagated as void context, but that's probably acceptable. If not, it's easy to adjust.

like image 60
ikegami Avatar answered Mar 22 '26 04:03

ikegami


You can do this with wantarray. (It is formatting wonky for me, too; sorry)

sub tryit{
    my $fun = shift;
    my $times = shift;
    my @args = @_;
    my $array_wanted = wantarray;
    my $ret;
    my @ret;
    do{  
    $times--;
    eval{
        if ($array_wanted) {
            @ret = $fun->(@args);
        }
        else {
            $ret = $fun->(@args);
        }
    };
    if($@){
        print "Error attemping cmd: $@\n";
    }
    else{
         if ($array_wanted) {
             return @ret;
         }
         else {
             return $ret;
         }
    }
    }while($times > 0);
    return;

}

I am sure a monster Perl hacker could find a way to tighten this up, but that is the basic idea.

like image 28
Nemo Avatar answered Mar 22 '26 06:03

Nemo