问题描述
在对各种事件做出反应的 Perl 守护进程中,我尝试使用 空对象模式在 2 种情况下,通过创建匿名子例程,它应该只返回值 1,也就是true"(请向右滚动以查看 LOGIN 和 的 check 子例程>ALIVE 事件):
In a Perl daemon reacting to various events I'm trying to use a Null object pattern in 2 cases by creating anonymous subroutines, which should just return a value of 1 aka "true" (please scroll to the right to see the check subroutines for LOGIN and ALIVE events):
package User;
our %EVENTS = (
LOGIN => {handler => \&handleLogin, check => sub {1}, },
CHAT => {handler => \&handleChat, check => \&mayChat, },
JOIN => {handler => \&handleJoin, check => \&mayJoin, },
LEAVE => {handler => \&handleLeave, check => \&mayLeave, },
ALIVE => {handler => sub {}, check => sub {1}, },
BID => {handler => \&handleBid, check => \&checkArgs, },
TAKE => {handler => \&handleTake, check => \&checkArgs, },
# .... more events ....
);
sub action($$$) {
my $user = shift;
my $event = shift;
my $arg = shift;
my $game = $user->{GAME};
unless (exists $EVENTS{$event}) {
print STDERR "wrong event: $event\n";
return;
}
my $handler = $EVENTS{$event}->{handler};
my $check = $EVENTS{$event}->{check};
return unless $user->$check->($arg); # XXX fails
$user->$handler->($arg);
}
sub mayChat($$) {
my $user = shift;
return if $user->{KIBITZER};
}
# ...... more methods here ...
1;
不幸的是,我收到 LOGIN 事件的运行时错误:
Unfortunately I get the runtime error for LOGIN event:
Can't use string ("1") as a subroutine ref while "strict refs" in use
有谁知道如何在这里修复它?
Does anybody please know how to fix it here?
如何为匿名 Perl 子程序提供函数指针"?
How to provide a "function pointer" to an anonymous Perl subroutine?
handler => \&sub { 1 } 也没有这样做.
在 CentOS 5.x 和 6.x 上使用 perl 5.8.8 和 perl 5.10.1
Using perl 5.8.8 and perl 5.10.1 on CentOS 5.x and 6.x
更新:
我也尝试过:
my $check = $EVENTS{$event}->{check};
return unless $check->($user, $arg);
但它没有帮助.我认为这排除了某些答案中建议的缺失的祝福".
but it doesn't help. I think this rules out the "missing blessing" suggested in some answers.
更新 2:
我在原始问题中扩展了源代码片段.背景是:我正在重构我的源代码,因此我创建了上面列出的 %EVENTS 哈希,这样对于每个传入的 event(通过 TCP 套接字从 Flash 客户端发送的字符串)存在对验证事件的子例程 (check) 的引用和对执行某些操作的另一个子例程 (handler) 的引用.我不确定其他子例程是否有效 - 我已经被第一个 LOGIN event 卡住了.
I have extended the source code snippet in my original question. The background is: I'm in the process of refactoring of my source code and thus I've created the %EVENTS hash as listed above, so that for each incoming event (a string sent over TCP-socket from a Flash client) there is a reference to a subroutine (check) which validates the event and a reference to another subroutine (handler) which performs some actions. I'm not sure if other subroutines work - I'm stuck already at the first LOGIN event.
我也不明白为什么 check => sub { 1 } 上面没有工作 - sub 不应该返回对匿名子例程的引用(当名称被省略时 - 根据 perldoc perlref 第 4 节)?
I also don't understand why doesn't check => sub { 1 } above work - isn't sub supposed to return a reference to an anonymous subroutine (when the name is omitted - according to perldoc perlref section 4)?
更新 3:
print Dumper(\%EVENTS)的输出-
$VAR1 = {
'PLAY' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'JOIN' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'OVER1' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'ALIVE' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'DISCARD' => {
'check' => $VAR1->{'PLAY'}{'check'},
'handler' => sub { "DUMMY" },
},
'MISS1' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'LOGIN' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'TAKE' => {
'check' => $VAR1->{'PLAY'}{'check'},
'handler' => sub { "DUMMY" },
},
'ONEMORE' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'OVER2' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'MISS2' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'EXACT' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'TRUST' => {
'check' => $VAR1->{'PLAY'}{'check'},
'handler' => sub { "DUMMY" },
},
'LEAVE' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'DEFEND' => {
'check' => $VAR1->{'PLAY'}{'check'},
'handler' => sub { "DUMMY" },
},
'OPEN' => {
'check' => $VAR1->{'PLAY'}{'check'},
'handler' => sub { "DUMMY" },
},
'REVEAL' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'CHAT' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'DECLARE' => {
'check' => $VAR1->{'PLAY'}{'check'},
'handler' => sub { "DUMMY" },
},
'BACK' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'MISERE' => {
'check' => sub { "DUMMY" },
'handler' => sub { "DUMMY" },
},
'BID' => {
'check' => $VAR1->{'PLAY'}{'check'},
'handler' => sub { "DUMMY" },
}
};
推荐答案
问题不在于出现问题的特定事件;实际的错误在 action
中.特别是这条线
The problem is not with the particular event that is surfacing the problem; the actual bug is in action
. In particular, the line
return unless $user->$check->($arg); # XXX fails
不会做你认为它会做的事情.在原型出现和 Perl 愿意尝试调用按名称指定的子程序之间,您最终会为 CHAT
事件调用 User::
.这似乎不是你想要做的.
doesn't do what you think it does. Between the presence of prototypes and Perl's willingness to try and call a sub specified by name, you wind up eventually calling User::
for the CHAT
event. Which doesn't seem to be what you intended for it to do.
更正确的调用看起来像
return unless $check->($user, $arg);
这期望 $check
包含一个子引用(它确实如此),取消引用它并调用它.即使有时 $check
会引用原型函数,这也有效.
This expects $check
to contain a subref (which it does), dereferences it and calls it. This works even though sometimes $check
will refer to a prototyped function.
这就留下了这个过程代码不尊重继承的问题.为此,您必须稍微改写 %EVENTS
.因此:
That leaves the problem that this procedural code doesn't respect inheritance. To do that, you have to rephrase %EVENTS
a bit. Thus:
our %EVENTS = (
LOGIN => {handler => \&handleLogin, check => sub {1}, },
CHAT => {handler => \&handleChat, check => sub { shift->mayChat(@_) },
...
);
请注意,强烈建议您不要将函数原型和 Perl OO 编程混合使用,因为这会导致像这样的难以诊断的问题.
Note that you are strongly discouraged to mix function prototypes and Perl OO programming precisely because it can lead to hard-to-diagnose problems like this one.
参考您的另一个问题:my $foo = sub { }
确实是您构造匿名子程序的方式.但是您确实需要适当地调用它们.
In reference to your other question: my $foo = sub { }
is indeed how you construct anonymous subroutines. But you do need to call them appropriately.
这篇关于不能使用字符串(“1")作为子例程 ref 而“strict refs"正在使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!