Correctly redirect return value to Perl

I'm a Perl newbie and have a hard time turning my head around my implicit type system. What I like is a simple wrapper function that has the same signature as the function it wraps so that it can be used in its place.

So, let's say I have an existing function orig

that I want to wrap. I accept multiple input arguments and have different return types based on those arguments. As long as I write the wrapper like this, the return types are the same as the original function, and it all works fine:

sub wrapper {
    my ($first) = @_;
    print "before. first argument: $first\n";
    return orig(@_);
}

      

However, if I want to execute some code in the shell after execution orig

, I don't know how I store the types. In my opinion, just like entering a perl function is always an array of scalars, so the output. Therefore, the solution should be like this:

sub wrapper {
    my ($first) = @_;
    print "before. first argument: $first\n";
    my @result = orig(@_);
    print "after";
    return @result;
}

      

But it doesn't work as expected. What am I missing? How do I write a wrapper function like this so that it works correctly for arbitrary return types?

+3


source to share


2 answers


In my opinion, just like entering a perl function is always an array of scalars, so this is the result.

No, not at all.

Perl functions can be called in a list context, in a scalar context, or in a void context.

some_function(@args);                  # void
my $result  = some_function(@args);    # scalar
my @results = some_function(@args);    # list

      

Many of Perl's built-in functions act differently depending on the context they call. For example, it grep

returns a list of results in the context of a list and the number of results in a scalar context.

If you are writing your own function and want to behave differently in different contexts, the function can use a keyword wantarray

to determine in which context it was called. wantarray

returns true for a list context, false for a scalar context, and undef for a void context.



Even if you don't consciously want to write a function that behaves differently according to the context, you can do so by accident by returning a context-sensitive expression such as grep

or map

, or an array (arrays in scalar context return their length).

The correct way to wrap a function without breaking the context is this. And yes, I understand that this is ugly.

sub wrapper {
   my ($first) = @_;
   print "before. first argument: $first\n";
   my @result = 
      wantarray         ? orig(@_) :
      defined wantarray ? scalar orig(@_) :
      do { orig(@_); () };
   print "after";
   wantarray ? @result : $result[0];
}

      

Now, if your wrapper doesn't need to modify @_

and doesn't need to modify the return values, then Class :: Method :: Modifiers can make it a little easier:

use Class::Method::Modifiers;

sub wrapper { orig(@_) }  # do nothing in the wrapper itself

before wrapper => sub {
   my ($first) = @_;
   print "before. first argument: $first\n";
};

after wrapper => sub {
   print "after";
};

      

+7


source


I suspect that the best way to do what you want is in Perl, but since you only specify the mechanism, I can only tell you how to make it work.

The return value of a Perl routine depends on the context of the call. Inside the subroutine, you can use the built-in operator wantarray

to detect the context - it will return true if the call was in the context of a list, and false otherwise.

So, to pass the value returned by another subroutine in the same context, you need to write wrapper

like this



sub wrapper {
    my ($first) = @_;

    print "before. first argument: $first\n";

    if (wantarray) {
      my @result = orig(@_);
      print "after";
      return @result;
    }
    else {
      my $result = orig(@_);
      print "after";
      return $result;
    }
}

      

But keep in mind that this routine is probably intended to be called in a scalar or contextual context. Writing something that behaves differently according to the context is rare, so you usually only want one of these two conditional branches depending on the behavior orig

.

+1


source







All Articles