У меня есть ряд служебных функций высшего порядка, которые берут ссылку на код и применяют этот код к некоторым данным. Некоторые из этих функций требуют локализации переменных во время выполнения подпрограмм. В начале я использовал caller
, чтобы определить, в какой пакет локализоваться, аналогично тому, как показано в этом примере reduce
function:
sub reduce (&@) {
my $code = shift;
my $caller = caller;
my ($ca, $cb) = do {
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
local (*a, *b) = local (*$ca, *$cb);
$a = shift;
while (@_) {
$b = shift;
$a = $code->()
}
$a
}
Первоначально эта техника работала нормально, однако, как только я попытался написать функцию-обертку для функции более высокого порядка, выяснить, как правильно вызвать вызывающего, становится сложно.
sub reduce_ref (&$) {&reduce($_[0], @{$_[1]})}
Теперь, чтобы reduce
заработал, мне нужно что-то вроде:
my ($ca, $cb) = do {
my $caller = 0;
$caller++ while caller($caller) =~ /^This::Package/;
no strict 'refs';
map \*{caller($caller).'::'.$_} => qw(a b)
};
В этот момент встал вопрос о том, какие пакеты пропустить, в сочетании с дисциплиной никогда не использовать функцию из этих пакетов. Должен был быть лучший способ.
Оказывается, что подпрограмма, которую функции высшего порядка принимают в качестве аргумента, содержит достаточно метаданных для решения проблемы. Мое текущее решение - использовать модуль интроспекции B
для определения тайника компиляции переданной подпрограммы. Таким образом, независимо от того, что происходит между компиляцией кода и его выполнением, функция высшего порядка всегда знает правильный пакет для локализации.
my ($ca, $cb) = do {
require B;
my $caller = B::svref_2object($code)->STASH->NAME;
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
Итак, мой последний вопрос: лучший ли это способ определения пакета вызывающего абонента в этой ситуации? Есть ли какой-то другой способ, о котором я не думал? Есть ли какая-то ошибка, ожидающая моего текущего решения?