Почему этот Perl выдает "Не ссылка на код"?
Мне нужно удалить метод из таблицы символов Perl во время выполнения. Я пытался сделать это с помощью undef &Square::area
, который действительно удаляет функцию, но оставляет некоторые следы позади. В частности, когда $square->area()
Perl жалуется, что это "не ссылка на код" вместо "неопределенная подпрограмма и квадрат:: область с именем", чего я и ожидаю.
Вы можете спросить: "Почему это важно? Вы удалили функцию, почему вы бы ее назвали?" Ответ в том, что я не называю это, Perl. Square наследуется от Rectangle, и я хочу, чтобы цепочка наследования прошла $square->area
Через &Rectangle::area
, но вместо пропуска Square, где метод не существует, а затем падения в область Rectangle (), вызов метода умирает с "Не ссылкой на CODE".
Как ни странно, это происходит только тогда, когда &Square::area была определена с помощью присваивания typeglob (например, *area = sub {...}
). Если функция определена с использованием стандарта sub area {}
подход, код работает как ожидалось.
Также интересно, что неопределенный весь шар работает, как и ожидалось. Просто не отмените саму подпрограмму.
Вот короткий пример, который иллюстрирует симптом и отличается от правильного поведения:
#!/usr/bin/env perl
use strict;
use warnings;
# This generates "Not a CODE reference". Why?
sub howdy; *howdy = sub { "Howdy!\n" };
undef &howdy;
eval { howdy };
print $@;
# Undefined subroutine &main::hi called (as expected)
sub hi { "Hi!\n" }
undef &hi;
eval { hi };
print $@;
# Undefined subroutine &main::hello called (as expected)
sub hello; *hello = sub { "Hello!\n" };
undef *hello;
eval { hello };
print $@;
Обновление: с тех пор я решил эту проблему с помощью Package::Stash (спасибо @Ether), но я все еще не понимаю, почему это происходит в первую очередь. perldoc perlmod
говорит:
package main;
sub Some_package::foo { ... } # &foo defined in Some_package
Это просто сокращение для присваивания typeglob во время компиляции:
BEGIN { *Some_package::foo = sub { ... } }
Но похоже, что это не просто условное обозначение, потому что эти два вызывают различное поведение после отмены определения функции. Я был бы признателен, если бы кто-то мог сказать мне, является ли это случаем (1) неправильных документов, (2) ошибки в Perl или (3) PEBCAK.
2 ответа
Манипулирование ссылками на таблицы символов само по себе неизбежно приведет к неприятностям, так как есть много маленьких сложностей, которые трудно понять правильно. К счастью, есть модуль, который делает всю тяжелую работу за вас, Package:: Stash - так что просто вызовите его методы add_package_symbol
а также remove_package_symbol
по мере необходимости.
Еще один хороший установщик методов, который вы, возможно, захотите проверить, это Sub:: Install - особенно хорошо, если вы хотите сгенерировать множество похожих функций.
Что касается того, почему ваш подход не верен, давайте посмотрим на таблицу символов после удаления ссылки на код:
sub foo { "foo!\n"}
sub howdy; *howdy = sub { "Howdy!\n" };
undef &howdy;
eval { howdy };
print $@;
use Data::Dumper;
no strict 'refs';
print Dumper(\%{"main::"});
отпечатки (сокращенно):
$ VAR1 = { 'Howdy' => *:: Howdy, 'foo' => *::foo, };
Как вы можете видеть, слот "привет" по-прежнему присутствует - неопределенный &howdy
на самом деле ничего не делает. Вы должны явно удалить слот glob, *howdy
,
Причина, по которой это происходит, заключается именно в том, что вы назначили типоглоб.
Когда вы удаляете символ CODE, остальная часть typeglob все еще задерживается, поэтому, когда вы попытаетесь выполнить howdy, он укажет наCODE
часть typeglob.