Как проверить, можно ли считать скаляр Perl подпрограммой? - PullRequest
6 голосов
/ 30 мая 2019

У меня есть API, который изначально принимал просто обычное постоянное целое число, но я расширяю его, чтобы вместо этого его можно было вычислить, если вместо этого передать ссылку на подпрограмму. Чтобы сохранить обратную совместимость, я тестирую, если это ссылка на код:

'CODE' eq ref($var)
    ? $var->()
    : $var;

но я понял, что исключает объекты, которые действуют как подпрограммы. Примерно так:

package Foo {
    sub new { bless {}, 'Foo' }
    use overload '&{}' => sub { sub { "Hello, world." } };
}

Я мог бы просто попытаться вызвать его внутри блока eval, но затем мне пришлось бы тщательно проверить $@, чтобы определить, не сработал ли он, потому что не являлся ссылкой на подпрограмму (таким образом, прибегая к простому скаляру) ) по сравнению с вызванной подпрограммой по какой-то причине не удалось (ошибка должна распространяться).

В этом случае, конечно, поскольку старое значение было нереференсным, я мог бы просто предположить, что любая ссылка может быть вызвана, но я бы хотел общее решение (как я использовал этот подход раньше, когда он использовался для быть хешрефом). Есть ли простой и надежный способ проверить, является ли скаляр «исполняемым»?

1 Ответ

5 голосов
/ 30 мая 2019

Вы можете проверить, пытаясь разыменовать его, но не запускать как подпрограмму.

my $is_code = ref $ref && do { local $@; eval { $ref = \&$ref; 1 } };

В этом случае вас не волнует, что это за ошибка, а была ли она одна. \&$ref достаточно, но, присваивая результат обратно $ref, вы можете избежать повторного вызова перегрузки при вызове этого coderef. Локализация $@ только для вежливости.

Как используется в моем модуле автозагрузки :: AUTOCAN , с первоначальным признанием haarg.

...