Об использовании массива функций в Perl - PullRequest
0 голосов
/ 04 августа 2010

Мы пытаемся создать API для автоматической поддержки commit () и rollback (), чтобы нам больше не приходилось с этим связываться.В результате исследования мы обнаружили, что использование eval {} - это путь.

Чтобы eval {} знал, что делать, я подумал о том, чтобы дать API массив функций, которые он может выполнять сforeach без API, который должен что-либо интерпретировать.Однако эта функция может быть в другом пакете.

Позвольте мне уточнить на примере:

sub handler {
    use OSA::SQL;
    use OSA::ourAPI;
    my @functions = ();
    push(@functions, OSA::SQL->add_page($date, $stuff, $foo, $bar));
    my $API = OSA::ourAPI->connect();
    $API->exec_multi(@functions);
}

Вопрос: возможно ли выполнить функции в @functions внутриOSA::ourAPI, даже если наш API не имеет use OSA::SQL.Если нет, было бы возможно, если бы я использовал ссылку на массив вместо массива, учитывая, что указатель будет указывать на известную функцию внутри памяти?

Примечание: это основная идея, которую мы хотимосновывать более сложную окончательную версию на.

1 Ответ

4 голосов
/ 04 августа 2010

  • Вы НЕ добавляете указатель на функцию в свой массив.Вы добавляете возвращаемое значение вызова подпрограммы add_page ().У вас есть 3 решения для этого:

    A.Вам нужно будет сохранить (в @functions) массив arrayrefs вида [\&OSA::SQL::add_page, @argument_values], что означает, что вы передаете фактическую ссылку на подпрограмму (называемую статически);и затем exec_multi сделает что-то вроде (синтаксис может быть не на 100% правильным, так как здесь 4 утра)

    sub exec_multi {
        my ($class, $funcs)= @_;
        foreach my $f (@$funcs) {
            my ($func, @args) = @$f;
            my $res = &$func(@args);
            print "RES:$res\n";
        }
    }
    

    Просто для повторения, это вызовет отдельные подпрограммы в статической версии (OSA::SQL::add_page), напримерБЕЗ передачи имени пакета в качестве первого параметра как вызов класса OSA::SQL->add_page.Если вы хотите последнее, см. Следующее решение.


    B.Если вы хотите вызывать ваши подпрограммы в контексте класса (как в вашем примере, другими словами, с именем класса в качестве первого параметра), вы можете использовать предложение ysth в комментарии.

    Вам нужно будет сохранить (в @functions) массив arrayrefs вида [sub { OSA::SQL->add_page(@argument_values) }], означающий, что вы передаете ссылку на подпрограмму, которая в свою очередь вызовет то, что вам нужно;и тогда exec_multi сделает что-то вроде (синтаксис может быть не на 100% правильным, так как здесь 4 утра)

    sub exec_multi {
        my ($class, $funcs)= @_;
        foreach my $f (@$funcs) {
            my ($func) = @$f;
            my $res = &$func();
            print "RES:$res\n";
        }
    }
    

    C.Вам нужно будет сохранить (в @functions) массив arrayrefs вида [ "OSA::SQL", "add_page", @argument_values], то есть вы передадите имя пакета и функции;и тогда exec_multi сделает что-то вроде (синтаксис может быть не на 100% правильным, так как здесь 4 утра)

    my ($package, $sub, @args) = @{ $functions[$i] };
    no strict 'refs';
    $package->$sub(@args);
    use strict 'refs';
    

  • Если я правильно понял ваш вопростогда вам не нужно беспокоиться о том, использует ли наш API-интерфейс OSA :: SQL, поскольку ваш основной код уже импортирует его.

    Однако, поскольку - в # 1B - вы будете передавать список пакетов в exec_multi в качестве первых элементов каждого arrayref, вы можете сделать «require $package; $package->import();» в exec_multi.Но опять же, это совершенно не нужно, если ваш вызов обработчика уже требуется и загружен каждый из этих пакетов.И чтобы сделать это правильно, вам нужно также передать список параметров в import().НО ЧТО ГГГГГГ?:)

...