在使用“strict refs”时,不能使用字符串(“1”)作为子程序ref

时间:2012-03-29 16:46:25

标签: perl perl5 perl5.10

在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" },
                   }
        };

6 个答案:

答案 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的接口,以便您是最后一个遇到此问题的人。