Какой самый надежный способ определить пакет coderef в Perl?

У меня есть ряд вспомогательных функций более высокого порядка, которые принимают ссылку на код и применяют этот код к некоторым данным. Некоторые из этих функций требуют локализации переменных во время выполнения подпрограмм. Вначале я использовал вызывающий , чтобы определить, в каком пакете локализоваться, аналогично тому, как показано в этом примере функция reduce :

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)
    };

At в этот момент встал вопрос о том, какие пакеты пропускать,в сочетании с дисциплиной никогда не использовать функции из этих пакетов. Должен был быть способ получше.

Оказывается, подпрограмма, которую функции высшего порядка принимают в качестве аргумента, содержит достаточно метаданных для решения проблемы. Мое текущее решение использует модуль интроспекции B для определения тайника компиляции переданной подпрограммы. Таким образом, независимо от того, что происходит между компиляцией кода и его выполнением, функция более высокого порядка всегда знает правильный пакет для локализации.

    my ($ca, $cb) = do {
        require B;
        my $caller = B::svref_2object($code)->STASH->NAME;
        no strict 'refs';
        map \*{$caller.'::'.$_} => qw(a b)
    };

Итак, мой главный вопрос заключается в том, является ли это лучшим способом определения пакета вызывающего в эта ситуация? Есть ли другой способ, о котором я не думал? Есть ли какая-то ошибка, ожидающая появления моего текущего решения?

10
задан Kara 18 March 2014 в 19:06
поделиться