Почему этот Perl выдает «Не ссылка на код»? - PullRequest
5 голосов
/ 13 января 2011

Мне нужно удалить метод из таблицы символов 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 ]

7 голосов
/ 13 января 2011

Манипулирование ссылками на таблицы символов само по себе неизбежно приведет к неприятностям, так как есть много маленьких сложностей, которые трудно понять правильно.К счастью, есть модуль, который выполняет всю тяжелую работу за вас, 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' все еще присутствует - не определен&howdy на самом деле ничего не делает .Вам необходимо явно удалить слот glob, *howdy.

2 голосов
/ 13 января 2011

Причина, по которой это происходит, заключается именно в том, что вы назначили глобальный тип.

Когда вы удаляете символ КОД, остальная часть типового глобуса все еще задерживается, поэтому, когда вы пытаетесь выполнить команду howdy, она будет указыватьCODE кусок typeglob.

...