为什么这个 Perl 会产生“不是代码引用?”

发布于 2024-10-12 01:30:46 字数 1567 浏览 7 评论 0原文

我需要在运行时从 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

郁金香雨 2024-10-19 01:30:46

自己操作符号表引用肯定会给您带来麻烦,因为有很多棘手的事情很难正确处理。幸运的是,有一个模块可以为您完成所有繁重的工作,Package::Stash - 所以就这样根据需要调用其方法 add_package_symbolremove_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::"});

prints (abridged):

    $VAR1 = {
              'howdy' => *::howdy,
              'foo' => *::foo,
    };

正如你所看到的, '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 and remove_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:

sub foo { "foo!\n"}
sub howdy; *howdy = sub { "Howdy!\n" };

undef &howdy;
eval { howdy };
print $@;

use Data::Dumper;
no strict 'refs';
print Dumper(\%{"main::"});

prints (abridged):

    $VAR1 = {
              'howdy' => *::howdy,
              'foo' => *::foo,
    };

As you can see, the 'howdy' slot is still present - undefining &howdy doesn't actually do anything enough. You need to explicitly remove the glob slot, *howdy.

蓝色星空 2024-10-19 01:30:46

发生这种情况的原因正是因为您分配了一个 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文