Невозможно использовать строку ("1") в качестве ссылки на подпрограмму, когда используются "строгие ссылки" - PullRequest
4 голосов
/ 29 марта 2012

В демоне Perl, реагирующем на различные события, я пытаюсь использовать шаблон пустого объекта в 2 случаях, создавая анонимные подпрограммы, которые должны просто возвращать значение 1 или «true» (пожалуйста, прокрутитевправо, чтобы увидеть подпрограммы check для событий LOGIN и 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;

К сожалению, я получаю ошибку времени выполнения для ВХОД событие:

Can't use string ("1") as a subroutine ref while "strict refs" in use

Кто-нибудь знает, пожалуйста, как это исправить здесь?

Как предоставить «указатель функции» на анонимную подпрограмму Perl?

Обработчик => \ & sub {1} тоже не делает этого.

Использование perl 5.8.8 и perl 5.10.1 в CentOS 5.x и6.x

ОБНОВЛЕНИЕ:

Я также попробовал следующее:

    my $check = $EVENTS{$event}->{check};
    return unless $check->($user, $arg);

, но это не помогает.Я думаю, что это исключает «недостающее благословение», предложенное в некоторых ответах.

ОБНОВЛЕНИЕ 2:

Я расширил фрагмент исходного кода в моем исходном вопросе. Фон: Я в процессе рефакторинга моего исходного кода и, таким образом, я создал хэш % EVENTS , как указано выше, так что для каждого входящего В событии (строка, отправляемая через TCP-сокет из клиента Flash ), имеется ссылка на подпрограмму ( check ), которая проверяет событие, и ссылку на другую подпрограмму ( обработчик ), который выполняет некоторые действия.Я не уверен, работают ли другие подпрограммы - я застрял уже на первом LOGIN событие .

Я также не понимаю, почему не check => sub {1} выше работы - не sub должен возвращать ссылку на анонимную подпрограмму (если имя опущено - согласно perldoc perlref раздел 4)?

ОБНОВЛЕНИЕ 3:

Выход Печать Dumper (\% СОБЫТИЯ) -

$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 ]

3 голосов
/ 30 марта 2012

Проблема не в конкретном событии, которое выходит из проблемы; фактическая ошибка в action. В частности, линия

    return unless $user->$check->($arg); # XXX fails

не делает то, что вы думаете, что делает. Между наличием прототипов и готовностью Perl попытаться вызвать подпрограмму, указанную по имени, вы в конечном итоге вызываете User:: для события CHAT. Похоже, это не то, что вы намеревались сделать.

Более правильный вызов выглядит как

    return unless $check->($user, $arg);

Ожидается, что $check будет содержать подреф (что он делает), разыменовывает его и вызывает его. Это работает, хотя иногда $check ссылается на функцию-прототип.

Это оставляет проблему в том, что этот процедурный кодекс не учитывает наследование. Для этого нужно немного перефразировать %EVENTS. Таким образом:

our %EVENTS = (
        LOGIN   => {handler => \&handleLogin,   check => sub {1},     },
        CHAT    => {handler => \&handleChat,    check => sub { shift->mayChat(@_) },
        ...
);

Обратите внимание, что вам не рекомендуется смешивать прототипы функций и программирование на Perl OO именно потому, что это может привести к трудным для диагностики проблемам, подобным этой.

В отношении вашего другого вопроса: my $foo = sub { } действительно, как вы создаете анонимные подпрограммы. Но вам нужно позвонить им соответствующим образом.

3 голосов
/ 29 марта 2012

$check уже является ссылкой на код, так что вы можете сказать

return unless $check->($arg);

Ваш существующий код также может быть спасен, если $check является ссылкой на код, который возвратил ссылку на код:

our %EVENTS = ( LOGIN => { ..., check => sub { sub { 1 } }, } ... );

Думайте о sub { } как об операторе «ссылки на код», так как \ является оператором для создания скалярной ссылки, или [...] является оператором для создания ссылки на массив.

2 голосов
/ 29 марта 2012

похоже, что $ event - это либо LOGIN, либо ALIVe, оба имеют анонимные подпрограммы для ключа проверки, которые возвращают 1. Для этой подпрограммы локально определяется $ check, и возвращается 1, затем код пытается получить доступ к значению '1' впользовательский хеш / объект как хеш

2 голосов
/ 29 марта 2012

Если $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.

Итак, выПридется более внимательно посмотреть на структуру ваших данных и избегать рассматривать скаляры как ссылки на код.

1 голос
/ 30 марта 2012

Мой собственный ответ - похоже, что следующее работает, я, вероятно, неправильно разыменовал свои субрефсы ...

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);
}
0 голосов
/ 29 марта 2012

Ключевой факт, касающийся вашей программы, который вы исключили из своего вопроса, таков: mayChat и все друзья возвращают ссылки на подпрограммы. Затем они оцениваются в

    return unless $user->$check->($arg); # XXX falis

. Записи в %EVENTS являются ссылками на подпрограммы, которые возвращают ссылки подпрограмм. Как только мы сформулируем это в этих терминах, проблема с вашим исходным кодом станет очевидной.

$EVENTS{LOGIN}->{check} - это ссылка на подпрограмму, которая возвращает целое число, когда ожидается, что это ссылка на подпрограмму, которая возвращает ссылку на подпрограмму. Если мы сделаем ссылку на подпрограмму, которая возвращает ссылку на подпрограмму:

    LOGIN   => {phase => undef,      handler => \&handleLogin,   check => sub { sub {1} },     },

, это работает.

Решение вашей проблемы состоит в том, чтобы (1) сделать эти записи подпрограммами, возвращающими подпрограммы, и (2) документировать интерфейс %EVENTS, чтобы вы были последним человеком, столкнувшимся с этой проблемой.

...