从perl子例程“按原样”存储返回值

时间:2015-03-12 02:29:54

标签: perl

我需要包装一个sub,以便在返回原始返回值之前触发事件之前/之后。一切正常,除非coderef返回一个数组,它将被强制转换为arrayref。有时我确实想要一个arrayref,所以我不能只检查ref类型并重新转换为数组。我还尝试使用仍然返回arrayref的wantarray

我目前正试图像这样做

$owner->methods->each(sub { # Methods is a hash container where each entry is a mapping of a method name to its implementation (as a coderef)
    my ($key, $val) = @_;

    return if $key eq 'trigger' || $key eq 'triggerBefore' || $key eq 'before' || $key eq 'on'; # Ignore decorating keys that would cause infinite callbacks
    $owner->methods->set($key, sub { # Replace the original sub with a version that calls before and after events
      my ($obj, @args) = @_;

      $owner->triggerBefore($key, @args);
      my $return = $val->(@args); # Return could be anything...
      $owner->trigger($key, $return);
      return $return;
    });
  });

我也尝试用以下内容替换回报,但无济于事:

return (wantarray && ref $return eq 'ARRAY') ? @$return : $return;

如果我不存储返回值而不是return $val->(@args);,那么一切都会正常工作(但之后我会失去""触发后)。有没有办法存储返回值"原样"而不是将其存储在标量中?

1 个答案:

答案 0 :(得分:1)

如果我理解正确,你的原始子程序在列表上下文中调用时会返回一个数组,而在标量上下文中调用时会返回一个arrayref。

您需要在调用者提供的同一调用上下文中调用包装的子,并存储并稍后根据需要返回其返回的值。这具有额外的优点,例如,在void上下文中调用时,让上下文感知子跳过冗长的计算。

这使包裹变得复杂。您可能还想传入@_,以防子进行任何修改:

sub {
  ...
  my $wa = wantarray;
  my @ret;

  ... trigger_before() ...

  unless (defined($wa)) {           # void
    $original_sub->(@_);
  } elsif (not $wa) {               # scalar
    $ret[0] = $original_sub->(@_);
  } else {                          # list
    @ret = $original_sub->(@_);
  }

  ... trigger_after() ...

  return unless defined($wa);
  return $wa ? @ret : $ret[0];
}