我需要包装一个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);
,那么一切都会正常工作(但之后我会失去""触发后)。有没有办法存储返回值"原样"而不是将其存储在标量中?
答案 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];
}