Sobre o uso de uma variedade de funções em Perl
Pergunta
Estamos tentando criar uma API para apoiar o Commit () e Rollback () automaticamente, para que não precisemos mais nos incomodar com isso. Pesquisando, descobrimos que usar eval {}
é o caminho a percorrer.
Por eval {}
Para saber o que fazer, pensei em dar à API uma variedade de funções, que ela pode executar com um foreach
sem a API ter que interpretar nada. No entanto, essa função pode estar em um pacote diferente.
Deixe -me esclarecer com um exemplo:
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);
}
A questão é: é possível executar as funções em @functions
dentro de OSA::ourAPI
, mesmo que nossapi não tenha use OSA::SQL
. Caso contrário, seria possível se eu usar uma referência de matriz em vez de uma matriz, já que o ponteiro apontaria para a função conhecida dentro da memória?
Nota: Esta é a ideia básica em que queremos basear a versão final mais complexa.
Solução
Você não está adicionando um ponteiro de função à sua matriz. Você está adicionando o valor de retorno de chamar a sub -rotina add_page (). Você tem 3 soluções para isso:
A. Você precisará armazenar (em
@functions
) uma variedade de ArrayRefs da forma[\&OSA::SQL::add_page, @argument_values]
, o que significa que você passa em uma referência real a uma sub -rotina (chamada estaticamente); E então Exec_multi fará algo como (a sintaxe pode não estar 100% correta, pois são 4 da manhã aqui)sub exec_multi { my ($class, $funcs)= @_; foreach my $f (@$funcs) { my ($func, @args) = @$f; my $res = &$func(@args); print "RES:$res\n"; } }
Apenas para reiterar, isso chamará de subs individual na versão estática (
OSA::SQL::add_page
), por exemplo, sem passar o nome do pacote como o primeiro parâmetro como uma chamada de classeOSA::SQL->add_page
gostaria. Se você quiser o último, consulte a próxima solução.
B. Se você deseja chamar seus subs em contexto de classe (como no seu exemplo, em outras palavras com o nome da classe como primeiro parâmetro), você pode usar a sugestão do YSTH no comentário.
Você precisará armazenar (em
@functions
) uma variedade de ArrayRefs da forma[sub { OSA::SQL->add_page(@argument_values) }]
, o que significa que você passa em uma referência a uma sub -rotina que, por sua vez, chamará o que você precisa; E então Exec_multi fará algo como (a sintaxe pode não estar 100% correta, pois são 4 da manhã aqui)sub exec_multi { my ($class, $funcs)= @_; foreach my $f (@$funcs) { my ($func) = @$f; my $res = &$func(); print "RES:$res\n"; } }
C. Você precisará armazenar (em
@functions
) uma variedade de ArrayRefs da forma[ "OSA::SQL", "add_page", @argument_values]
, o que significa que você passa em um pacote e nome da função; E então Exec_multi fará algo como (a sintaxe pode não estar 100% correta, pois são 4 da manhã aqui)my ($package, $sub, @args) = @{ $functions[$i] }; no strict 'refs'; $package->$sub(@args); use strict 'refs';
Se eu entendi sua pergunta corretamente, você não precisa se preocupar se o OurApi usa OSA :: SQL, já que o seu código principal já a importa.
No entanto, desde - no #1B - você passará uma lista de pacotes para exec_multi como os primeiros elementos de cada arrayref, você pode fazer "
require $package; $package->import();
"Em Exec_multi. Mas, novamente, é completamente desconthery se o seu manipulador já é necessário e carregar cada um desses pacotes. E para fazê-lo corretamente, você precisa passar em uma lista de parâmetros paraimport()
também. Mas por queyyyyy? :)