为什么这个 Perl 会产生“不是代码引用?”
我需要在运行时从 Perl 符号表中删除一个方法。我尝试使用 undef &Square::area
来执行此操作,它确实删除了该函数,但留下了一些痕迹。具体来说,当调用 $square->area() 时,Perl 抱怨它是“不是 CODE 引用”,而不是我所期望的“未定义的子例程 &Square::area 调用” 。
您可能会问,“为什么这很重要?您删除了该函数,为什么还要调用它?”答案是我没有调用它,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 说:
包主程序;
sub Some_package::foo { ... } # &foo 在 Some_package 中定义
这只是编译时 typeglob 赋值的简写:
BEGIN { *Some_package::foo = sub { ... } }
但它似乎不仅仅是简写,因为两者在取消定义函数后会导致不同的行为。如果有人能告诉我这是否是(1)不正确的文档,(2)perl 中的错误,或(3)PEBCAK 的情况,我将不胜感激。
I need to remove a method from the Perl symbol table at runtime. I attempted to do this using undef &Square::area
, which does delete the function but leaves some traces behind. Specifically, when $square->area()
is called, Perl complains that it is "Not a CODE reference" instead of "Undefined subroutine &Square::area called" which is what I expect.
You might ask, "Why does it matter? You deleted the function, why would you call it?" The answer is that I'm not calling it, Perl is. Square inherits from Rectangle, and I want the inheritance chain to pass $square->area
through to &Rectangle::area
, but instead of skipping Square where the method doesn't exist and then falling through to Rectangle's area(), the method call dies with "Not a CODE reference."
Oddly, this appears to only happen when &Square::area was defined by typeglob assignment (e.g. *area = sub {...}
). If the function is defined using the standard sub area {}
approach, the code works as expected.
Also interesting, undefining the whole glob works as expected. Just not undefining the subroutine itself.
Here's a short example that illustrates the symptom, and contrasts with correct behavior:
#!/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 $@;
Update: I have since solved this problem using Package::Stash (thanks @Ether), but I'm still confused by why it's happening in the first place. perldoc perlmod
says:
package main;
sub Some_package::foo { ... } # &foo defined in Some_package
This is just a shorthand for a typeglob assignment at compile time:
BEGIN { *Some_package::foo = sub { ... } }
But it appears that it isn't just shorthand, because the two cause different behavior after undefining the function. I'd appreciate if someone could tell me whether this is a case of (1) incorrect docs, (2) bug in perl, or (3) PEBCAK.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
自己操作符号表引用肯定会给您带来麻烦,因为有很多棘手的事情很难正确处理。幸运的是,有一个模块可以为您完成所有繁重的工作,Package::Stash - 所以就这样根据需要调用其方法
add_package_symbol
和remove_package_symbol
。您可能想要查看的另一个好方法安装程序是 Sub::Install - 特别好,如果您想要生成很多类似的函数。
至于为什么你的方法不正确,让我们看一下删除代码引用后的符号表:
prints (abridged):
正如你所看到的, 'howdy' 槽仍然存在 - undefining
&howdy< /code> 实际上并没有做足够的
任何事情。您需要显式删除 glob 槽*howdy
。Manipulating symbol table references yourself is bound to get you into trouble, as there are lots of little fiddly things that are hard to get right. Fortunately there is a module that does all the heavy lifting for you, Package::Stash -- so just call its methods
add_package_symbol
andremove_package_symbol
as needed.Another good method installer that you may want to check out is Sub::Install -- especially nice if you want to generate lots of similar functions.
As to why your approach is not correct, let's take a look at the symbol table after deleting the code reference:
prints (abridged):
As you can see, the 'howdy' slot is still present - undefining
&howdy
doesn't actually doanythingenough. You need to explicitly remove the glob slot,*howdy
.发生这种情况的原因正是因为您分配了一个 typeglob。
当您删除 CODE 符号时,typeglob 的其余部分仍然存在,因此当您尝试执行 howdy 时,它将指向 typeglob 的非
CODE
部分。The reason it happens is precisely because you assigned a typeglob.
When you delete the CODE symbol, the rest of typeglob is still lingering, so when you try to execute howdy it will point to the non-
CODE
piece of typeglob.