在Perl守护进程中对各种事件作出反应我试图在2种情况下使用a Null object pattern创建匿名子例程,它应该返回值1也称为“true”(请向右滚动以查看登录和 ALIVE 事件的检查子例程:
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 事件的运行时错误:
Can't use string ("1") as a subroutine ref while "strict refs" in use
有人请知道如何解决这个问题吗?
如何为匿名Perl子程序提供“函数指针”?
handler => \& sub {1} 也不会这样做。
在CentOS 5.x和6.x上使用perl 5.8.8和perl 5.10.1
更新
我也试过以下:
my $check = $EVENTS{$event}->{check};
return unless $check->($user, $arg);
但它没有帮助。我认为这排除了一些答案中提出的“失踪的祝福”。
更新2:
我在原始问题中扩展了源代码段。 背景是:我正在重构我的源代码,因此我创建了如上所列的%EVENTS 哈希,以便每次传入< em> event (通过TCP-socket从a Flash client发送的字符串)有一个子程序(检查)的引用,它验证事件和对另一个子程序的引用(执行某些操作的处理程序)。我不确定其他子程序是否有效 - 我已经停留在第一个 LOGIN 事件。
我也不明白为什么不 check =&gt;上面工作的子{1} - 不是 sub 应该返回对匿名子程序的引用(当省略名称时 - 根据perldoc perlref第4节)?< / p>
更新3:
打印转储器(\%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" },
}
};
答案 0 :(得分:3)
$check
已经是代码参考,所以你可以说
return unless $check->($arg);
如果$check
是对返回代码引用的代码的引用,也可以抢救现有代码:
our %EVENTS = ( LOGIN => { ..., check => sub { sub { 1 } }, } ... );
将sub { }
视为“代码引用”运算符,\
是创建标量引用的运算符,或[...]
是创建数组引用的运算符。< / p>
答案 1 :(得分:3)
问题不在于出现问题的特定事件;实际的错误在action
。特别是,行
return unless $user->$check->($arg); # XXX fails
不符合您的想法。在原型的存在和Perl尝试调用名称指定的子的意愿之间,最终为User::
事件调用CHAT
。这似乎不是你打算做的。
更正确的通话看起来像
return unless $check->($user, $arg);
这期望$check
包含一个subref(它会),解除引用并调用它。即使有时$check
将引用原型函数,这仍然有效。
这留下了这个程序代码不尊重继承的问题。要做到这一点,你必须改写一下%EVENTS
。因此:
our %EVENTS = (
LOGIN => {handler => \&handleLogin, check => sub {1}, },
CHAT => {handler => \&handleChat, check => sub { shift->mayChat(@_) },
...
);
请注意,强烈建议您不要将函数原型和Perl OO编程精确混合,因为它可能会导致难以诊断的问题,例如此问题。
参考你的另一个问题:my $foo = sub { }
确实是你如何构建匿名子程序。但你需要适当地调用它们。
答案 2 :(得分:2)
除非$check
是代码参考,否则
$user->$check->($arg);
无效。
作为一个独立的例子:
> perl -e 'use strict;use warnings;my $a=17;$a->("whatever");'
Can't use string ("17") as a subroutine ref while "strict refs" in use at -e line 1.
因此,您必须更仔细地查看数据结构,并避免将标量视为代码引用。
答案 3 :(得分:2)
看起来$ event是LOGIN或ALIVe,两者都有返回1的检查键的匿名subs。$ check在本地定义到该子例程,并返回1,然后代码尝试访问值'1'用户哈希/对象作为哈希
答案 4 :(得分:1)
我自己的答案 - 以下似乎有效,我可能以错误的方式解除引用我的子参考...
sub action($$$) {
my $user = shift;
my $event = shift;
my $arg = shift;
my $handler = $EVENTS{$event}->{handler};
my $check = $EVENTS{$event}->{check};
return unless &$check($user, $arg);
&$handler($user, $arg);
}
答案 5 :(得分:0)
关于你的程序的关键事实是你遗漏了问题:mayChat
和朋友都返回子程序引用。然后在
return unless $user->$check->($arg); # XXX falis
。 %EVENTS
中的条目是对返回子例程引用的例程的引用。一旦我们用这些术语说明,原始代码的问题就变得很明显了。
$EVENTS{LOGIN}->{check}
是对返回整数的sub的引用,当期望的是对返回对sub的引用的sub的引用时。如果我们使它成为对返回对sub的引用的sub的引用:
LOGIN => {phase => undef, handler => \&handleLogin, check => sub { sub {1} }, },
,它有效。
您的问题的解决方法是(1)使这些条目成为返回子的子节点,并且(2)记录%EVENTS
的接口,以便您是最后一个遇到此问题的人。