5

我需要在运行时从 Perl 符号表中删除一个方法。我尝试使用 来执行此操作undef &Square::area,它确实删除了该功能,但留下了一些痕迹。具体来说,当$square->area()被调用时,Perl 抱怨它是“不是代码引用”而不是“未定义的子例程 &Square::area called”,这是我所期望的。

你可能会问,“为什么重要?你删除了这个函数,你为什么要调用它?” 答案是我不叫它,Perl 是。Square 继承自 Rectangle,我希望继承链传递$square->area&Rectangle::area,但不是跳过该方法不存在的 Square 然后落入 Rectangle 的 area(),而是方法调用以“不是 CODE 引用”而终止。

奇怪的是,这似乎只发生在 &Square::area 由 typeglob 赋值定义时(例如*area = sub {...})。如果函数是使用标准sub area {}方法定义的,则代码将按预期工作。

同样有趣的是,取消定义整个 glob 按预期工作。只是不取消定义子程序本身。

这是一个简短的示例,说明了症状,并与正确的行为形成对比:

#!/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,我将不胜感激。

4

2 回答 2

7

自己操作符号表引用肯定会给你带来麻烦,因为有很多小事很难做对。幸运的是,有一个模块可以为您完成所有繁重的工作,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 = {
              '你好' => *::你好,
              'foo' => *::foo,
    };

正如您所看到的,'howdy' 插槽仍然存在 - 未定义&howdy实际上并没有做任何足够的事情。您需要显式删除 glob 插槽,*howdy.

于 2011-01-12T22:17:31.110 回答
2

它发生的原因正是因为您分配了一个 typeglob。

当您删除 CODE 符号时,typeglob 的其余部分仍然存在,因此当您尝试执行 howdy 时,它将指向非CODEtypeglob 的一部分。

于 2011-01-12T22:24:18.423 回答