index | project | pods | responsabili |
NOMEperlembed - come integrare perl nei vostri programmi C
DESCRIZIONE
PREAMBOLOVolete:
SOMMARIO
Compilazione del programma CSe avete problemi a compilare gli script in questo documento, non siete i soli. La regola d'oro è: COMPILATE I PROGRAMMA NELLA STESSA, ESATTA MANIERA IN CUI È STATO COMPILATO IL VOSTRO PERL. (Scusate l'urlo). Inoltre, ogni programma C che utilizza Perl deve effettuare il link alla libreria perl. Cos'é, chiederete? Perl stesso è scritto in C; la libreria perl è la raccolta dei programmi C compilati che sono stati utilizzati per creare il vostro eseguibile perl (/usr/bin/perl o equivalenti). (Corollario: non potete utilizzare Perl dal vostro programma C se Perl non è stato compilato sulla vostra macchina, o perlomeno installato in maniera corretta -- questo è il motivo per cui non dovreste copiare alla leggera gli eseguibili Perl da una macchina all'altra senza copiare anche la directory lib.) Quando utilizzate Perl da C, il vostro programma C deve -- normalmente -- allocare, ``lanciare'' e deallocare un oggetto PerlInterpreter (InterpretePerl, N.d.T.), che è definito nella libreria perl. Se avete a disposizione una copia di Perl sufficientemente recente da contenere questa documentazione (versione 5.002 o successive), allora la libreria perl (e gli header file EXTERN.h e perl.h, di cui avrete bisogno) risiederanno in una directory che ha la forma seguente: /usr/local/lib/perl5/architettura_vostra_macchina/CORE o forse solo /usr/local/lib/perl5/CORE o anche qualcosa tipo /usr/opt/perl5/CORE Per avere un suggerimento su dove trovare CORE, eseguite il comando: perl -MConfig -e 'print $Config{archlib}' Ecco come dovreste compilare gli esempi della prossima sezione, Aggiunta di un interprete Perl al vostro programma C, su una macchina Linux: % gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/local/lib/perl5/i586-linux/5.003/CORE -L/usr/local/lib/perl5/i586-linux/5.003/CORE -o interp interp.c -lperl -lm (Ovviamente tutto su una sola riga). Su un DEC ALpha provvisto della vecchia 5.003_05, ci sono delle lievi differenze: % cc -O2 -Olimit 2900 -DSTANDARD_C -I/usr/local/include -I/usr/local/lib/perl5/alpha-dec_osf/5.00305/CORE -L/usr/local/lib/perl5/alpha-dec_osf/5.00305/CORE -L/usr/local/lib -D__LANGUAGE_C__ -D_NO_PROTO -o interp interp.c -lperl -lm Come fate a capire cosa aggiungere? Assumendo che stiate utilizzando
una versione di Perl successiva alla 5.001, lanciate il comando
Dovrete scegliere il compilatore appropriato (cc, gcc, et al.) per
la vostra macchina: Dovrete anche scegliere la directory di libreria corretta (tipo
/usr/local/lib/...) per la vostra macchina. Se il compilatore si
lamenta che certe funzioni non sono definite, o che non riesce a trovare
-lperl, dovete cambiare il percorso che segue Potreste aver bisogno di aggiungere anche altre librerie. Quali? Con buona probabilità quelle stampate da perl -MConfig -e 'print $Config{libs}' Ammesso che il vostro binario perl sia stato configurato ed installato correttamente, il modulo ExtUtils::Embed è in grado di determinare questa informazione per voi: % cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts` Se ExtUtils::Embed non fa parte della vostra distribuzione Perl, potete prenderlo da http://www.perl.com/perl/CPAN/modules/by-module/ExtUtils/ . (Se questo documento si trova nella vostra distribuzione Perl, allora state utilizzando almeno la versione 5.004 ed avete già il modulo). Il kit ExtUtils::Embed kit su CPAN contiene anche il codice sorgente per gli esempi di questo documento, i test, esempi aggiuntivi ed altre informazioni che potreste trovare utili.
Aggiunta di un interprete Perl al vostro programma CIn un certo senso, perl (il programma C) è un buon esempio di integrazione di Perl (il linguaggio), per cui andremo a dimostrare l'embedding con miniperlmain.c, incluso nella distribuzione sorgente. Quella che segue è una versione cannibalizzata e non portabile di miniperlmain.c, contenente l'essenziale per l'integrazione: #include <EXTERN.h> /* dalla distribuzione Perl */ #include <perl.h> /* dalla distribuzione Perl */ static PerlInterpreter *my_perl; /*** L'interprete Perl ***/ int main(int argc, char **argv, char **env) { PERL_SYS_INIT3(&argc,&argv,&env); my_perl = perl_alloc(); perl_construct(my_perl); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_parse(my_perl, NULL, argc, argv, (char **)NULL); perl_run(my_perl); perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); } Da notare che non stiamo utilizzando il puntatore Ora compilate questo programma (lo chiameremo interp.c) in un eseguibile: % cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts` Dopo una compilazione valida sarete in grado di utilizzare interp come se fosse perl stesso: % interp print "Perl ci piace un sacco\n"; print "10890 - 9801 is ", 10890 - 9801; <CTRL-D> Perl ci piace un sacco 10890 - 9801 is 1089 o anche: % interp -e 'printf("%x", 3735928559)' deadbeef Potete anche leggere ed eseguire istruzioni Perl da un file nel mezzo del vostro programma C, inserendo il nome del file dentro argv[1] prima di chiamare perl_run().
Chiamare una subroutine Perl dal vostro programma CPer chiamare le singole subroutine Perl potete utilizzare una qualunque
delle funzioni call_* documentate in perlcall. In questo esempio
utilizzeremo Ci riferiremo al programma dimostrativo che segue con il nome mostratempo.c. #include <EXTERN.h> #include <perl.h> static PerlInterpreter *my_perl; int main(int argc, char **argv, char **env) { char *args[] = { NULL }; PERL_SYS_INIT3(&argc,&argv,&env); my_perl = perl_alloc(); perl_construct(my_perl); perl_parse(my_perl, NULL, argc, argv, NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; /*** saltiamo perl_run() ***/ call_argv("mostratempo", G_DISCARD | G_NOARGS, args); perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); } dove mostratempo è una subroutine Perl che non prende argomenti (è G_NOARGS) e per la quale ignoreremo il valore restituito (per questo motivo utilizziamo G_DISCARD). Questi ed altri indicatori sono discussi in dettaglio in perlcall. Definiremo la subroutine mostratempo in un file chiamato mostratempo.pl: print "Questa stringa non viene stampata."; sub mostratempo { print time; } Piuttosto semplice. Ora compilate e lanciate: % cc -o mostratempo mostratempo.c `perl -MExtUtils::Embed -e ccopts -e ldopts` % mostratempo mostratempo.pl 818284590 che fornisce il numero di secondi passati dal primo gennaio 1970 (l'inizio della epoch di Unix) al momento in cui il programma è stato avviato durante la stesura della versione inglese di questo documento. In questo caso particolare non abbiamo bisogno di chiamare perl_run(), poiché impostiamo il PL_exit_flag PERL_EXIT_DESTRUCT_END per fare in modo che vengano eseguiti i blocchi END quando viene chiamata perl_destruct(). Se avete bisogno di passare argomenti alla funzione Perl, potete aggiungere
delle stringhe alla lista
Valutare un'istruzione Perl dal vostro programma CPerl mette a disposizione due funzioni da API per valutare porzioni di codice Perl: perlapi/eval_sv e perlapi/eval_pv. In effetti, queste sono le sole funzioni di cui avrete mai bisogno per eseguire porzioni di codice Perl dai vostri programmi C. Il codice (Perl) da valutare puà essere lungo quanto volete; può contenere più istruzioni, può utilizzare perlfunc/use, perlfunc/require e perlfunc/do per includere file Perl esterni. eval_pv ci permette di valuatare stringhe Perl individuali, per poi
estrarre variabili per l'inserimento in tipi C. Il programma che segue,
stringhe.c, esegue tre stringhe di codice Perl, estraendo un
#include <EXTERN.h> #include <perl.h> static PerlInterpreter *my_perl; main (int argc, char **argv, char **env) { STRLEN n_a; char *embedding[] = { "", "-e", "0" }; PERL_SYS_INIT3(&argc,&argv,&env); my_perl = perl_alloc(); perl_construct( my_perl ); perl_parse(my_perl, NULL, 3, embedding, NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_run(my_perl); /** Considera $a come un intero **/ eval_pv("$a = 3; $a **= 2", TRUE); printf("a = %d\n", SvIV(get_sv("a", FALSE))); /** Considera $a come un float **/ eval_pv("$a = 3.14; $a **= 2", TRUE); printf("a = %f\n", SvNV(get_sv("a", FALSE))); /** Considera $a come una stringa **/ eval_pv("$a = 'rekcaH lreP rehtonA tsuJ'; $a = reverse($a);", TRUE); printf("a = %s\n", SvPV(get_sv("a", FALSE), n_a)); perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); } Tutte quelle strane funzioni con sv nel nome aiutano nella conversione di scalari Perl in tipi C. Sono spiegati in perlguts e perlapi. Se compilate ed avviate stringhe.c, potrete vedere i risultati
dell'utilizzo di SvIV() per generare un a = 9 a = 9.859600 a = Just Another Perl Hacker Nell'esempio riportato, abbiamo creato una variabile globale per contenere temporaneamente il valore calcolato della nostra espressione valutata. È anche possibile, e nella maggior parte dei casi consigliabile, raccogliere direttamente il valore di ritorno di eval_pv() invece di passare per una variabile temporanea: ... STRLEN n_a; SV *val = eval_pv("reverse 'rekcaH lreP rehtonA tsuJ'", TRUE); printf("%s\n", SvPV(val,n_a)); ... In questo modo evitiamo di sporcare il namespace perché non creiamo variabili globali; come bonus aggiunto abbiamo anche semplificato il nostro codice.
Effettuare pattern-match e sostituzioni Perl dal vostro programma CLa funzione eval_sv() ci permette di valutare stringhe di codice Perl, di modo che possiamo definire alcune funzioni specializzate in matching e sostituzioni: match(), sostituisci() e match_multipli(). I32 match(SV *string, char *pattern); Data una stringa ed un pattern (per esempio, int sostituisci(SV **string, char *pattern); Dato un puntatore ad un int match_multipli(SV *string, char *pattern, AV **matches); Dato un Ecco un programma di esempio, match.c, che implementa ed utilizza tutte e tre le funzioni (le linee lunghe sono state mandate a capo): #include <EXTERN.h> #include <perl.h> static PerlInterpreter *my_perl; /** my_eval_sv(code, error_check) ** una specie di eval_sv(), ** ma eliminiamo il valore di ritorno dallo stack **/ SV* my_eval_sv(SV *sv, I32 croak_se_errore) { dSP; SV* retval; STRLEN n_a; PUSHMARK(SP); eval_sv(sv, G_SCALAR); SPAGAIN; retval = POPs; PUTBACK; if (croak_se_errore && SvTRUE(ERRSV)) croak(SvPVx(ERRSV, n_a)); return retval; } /** match(string, pattern) ** ** Utilizzata per match in contesto scalare. ** ** Restituisce 1 se il match ha successo; 0 altrimenti. **/ I32 match(SV *string, char *pattern) { SV *command = NEWSV(1099, 0), *retval; STRLEN n_a; sv_setpvf(command, "my $string = '%s'; $string =~ %s", SvPV(string,n_a), pattern); retval = my_eval_sv(command, TRUE); SvREFCNT_dec(command); return SvIV(retval); } /** sostituisci(string, pattern) ** ** Utilizzata per operazioni =~ che modificano il termine a sinistra ** (s/// e tr///). ** ** Restituisce il numero di corrispondenze trovate, modificando la ** stringa in ingresso **/ I32 sostituisci(SV **string, char *pattern) { SV *command = NEWSV(1099, 0), *retval; STRLEN n_a; sv_setpvf(command, "$string = '%s'; ($string =~ %s)", SvPV(*string,n_a), pattern); retval = my_eval_sv(command, TRUE); SvREFCNT_dec(command); *string = get_sv("string", FALSE); return SvIV(retval); } /** match_multipli(string, pattern, matches) ** ** Utilizzata per match in contesto lista. ** ** Restituisce il numero di corrispondenze, e riempie ** **matches con le sottostringhe trovate. **/ I32 matches(SV *string, char *pattern, AV **match_list) { SV *command = NEWSV(1099, 0); I32 num_matches; STRLEN n_a; sv_setpvf(command, "my $string = '%s'; @array = ($string =~ %s)", SvPV(string,n_a), pattern); my_eval_sv(command, TRUE); SvREFCNT_dec(command); *match_list = get_av("array", FALSE); num_matches = av_len(*match_list) + 1; /** assume $[ is 0 **/ return num_matches; } main (int argc, char **argv, char **env) { char *embedding[] = { "", "-e", "0" }; AV *match_list; I32 num_matches, i; SV *text; STRLEN n_a; PERL_SYS_INIT3(&argc,&argv,&env); my_perl = perl_alloc(); perl_construct(my_perl); perl_parse(my_perl, NULL, 3, embedding, NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; text = NEWSV(1099,0); sv_setpv(text, "When he is at a convenience store and the " "bill comes to some amount like 76 cents, Maynard is " "aware that there is something he *should* do, something " "that will enable him to get back a quarter, but he has " "no idea *what*. He fumbles through his red squeezey " "changepurse and gives the boy three extra pennies with " "his dollar, hoping that he might luck into the correct " "amount. The boy gives him back two of his own pennies " "and then the big shiny quarter that is his prize. " "-RICHH"); if (match(text, "m/quarter/")) /** 'quarter' contenuto? **/ printf("match: Il testo contiene la parola 'quarter'.\n\n"); else printf("match: Il testo non contiene la parola 'quarter'.\n\n"); if (match(text, "m/eighth/")) /** eighth'? **/ printf("match: Il testo contiene la parola 'eighth'.\n\n"); else printf("match: Il testo non contiene la parola 'eighth'.\n\n"); /** Match tutte le occorreze di /wi../ **/ num_matches = match_multipli(text, "m/(wi..)/g", &match_list); printf("match_multipli: m/(wi..)/g ha trovato %d match...\n", num_matches); for (i = 0; i < num_matches; i++) printf("match: %s\n", SvPV(*av_fetch(match_list, i, FALSE),n_a)); printf("\n"); /** Rimuovi tutte le vocali dal testo **/ num_matches = sostituisci(&text, "s/[aeiou]//gi"); if (num_matches) { printf("sostituisci: s/[aeiou]//gi...%d sostituzioni effettuate.\n", num_matches); printf("Ora il testo risulta: %s\n\n", SvPV(text,n_a)); } /** Tenta una sostituzione **/ if (!sostituisci(&text, "s/Perl/C/")) { printf("sostituisci: s/Perl/C/...Nessuna sostituzione.\n\n"); } SvREFCNT_dec(text); PL_perl_destruct_level = 1; perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); } che produce l'uscita (di nuovo, le righe lunghe sono state tagliate) match: Il testo contiene la parola 'quarter'. match: Il testo non contiene la parola 'eighth'. match_multipli: m/(wi..)/g ha trovato 2 matches... match: will match: with sostituisci: s/[aeiou]//gi...139 sostituzioni effettuate. Ora il testo risulta: Whn h s t cnvnnc str nd th bll cms t sm mnt lk 76 cnts, Mynrd s wr tht thr s smthng h *shld* d, smthng tht wll nbl hm t gt bck qrtr, bt h hs n d *wht*. H fmbls thrgh hs rd sqzy chngprs nd gvs th by thr xtr pnns wth hs dllr, hpng tht h mght lck nt th crrct mnt. Th by gvs hm bck tw f hs wn pnns nd thn th bg shny qrtr tht s hs prz. -RCHH sostituisci: s/Perl/C/...Nessuna sostituzione.
Manipolare lo stack Perl dal vostro programma CQuando si trovano a dover spiegare le pile (stack, N.d.T.), la maggior parte dei libri di informatica farfugliano qualcosa riguardo a pile di piatti in una caffetteria: l'ultimo che mettete (push) sulla pila è il primo che poi andate a togliere (pop) quando vi serve un piatto. Per i nostri scopi andrà bene questa spiegazione: il vostro programma C immetterà alcuni parametri sullo ``stack Perl'', chiuderà gli occhi finché non sarà accaduta qualche magia, e poi estrarrà i risultati -- il valore di ritorno della vostra funzione Perl -- dallo ``stack''. (Utilizziamo la parola inglese stack invece di pila poiché questa notazione è quella più utilizzata quando si parla delle pile di attivazione che controllano l'evoluzione dei programmi, N.d.T.). Prima di tutto avete bisogno di sapere come effettuare la conversione
tra i tipi
C e quelli Perl, utilizzando Successivamente, avete bisogno di sapere come manipolare lo stack di Perl. Questo è descritto in perlcall. Una volta che avete capito tutto questo, integrare Perl dentro C è facile. Poiché C non ha alcuna funzione nativa per l'elevazione a potenza degli interi, rendiamogli disponibile l'operatore Perl ** (il che è meno utile di quel che può sembrare, poiché Perl implementa ** con la funzione C pow()). Prima di tutto creeremo una funzione di elevazione a potenza di comodo in power.pl: sub expo { my ($a, $b) = @_; return $a ** $b; } Successivamente creeremo il programma, potenza.c, con una funzione PotenzaPerl() che contiene tutti i perlguts necessari ad inserire due parametri (argomenti) dentro expo e per estrarne il valore. Fate un bel respiro... #include <EXTERN.h> #include <perl.h> static PerlInterpreter *my_perl; static void PotenzaPerl(int a, int b) { dSP; /* inizializza lo stack pointer */ ENTER; /* quanto viene creato da qui... */ SAVETMPS; /* ..e` una variabile temporanea */ PUSHMARK(SP); /* segnati lo stack pointer */ XPUSHs(sv_2mortal(newSViv(a))); /* inserisci la base sullo stack */ XPUSHs(sv_2mortal(newSViv(b))); /* poi l'esponente */ PUTBACK; /* stack pointer da locale a globale */ call_pv("expo", G_SCALAR); /* chiama la funzione */ SPAGAIN; /* aggiorna lo stack pointer */ /* prendi il valore dallo stack */ printf ("%d alla %d fa %d.\n", a, b, POPi); PUTBACK; FREETMPS; /* rilascia il valore di ritorno */ LEAVE; /* ...e gli argomenti "mortali". */ } int main (int argc, char **argv, char **env) { char *my_argv[] = { "", "power.pl" }; PERL_SYS_INIT3(&argc,&argv,&env); my_perl = perl_alloc(); perl_construct( my_perl ); perl_parse(my_perl, NULL, 2, my_argv, (char **)NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_run(my_perl); PotenzaPerl(3, 4); /*** Calcola 3 ** 4 ***/ perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); } Compilate ed eseguite: % cc -o potenza potenza.c `perl -MExtUtils::Embed -e ccopts -e ldopts` % potenza 3 alla 4 fa 81.
Mantenere un interprete persistenteQuando sviluppate applicazioni interattive, o che potenzialmente possono avere una vita molto lunga, è una buona idea mantenere un interprete Perl persistente invece che allocarlo e costruirne uno nuovo più e più volte. La ragione principale è la velocità: in questo modo Perl viene caricato in memoria solo una volta. In ogni caso, dovete prestare maggiore attenzione con namespace
e scoping delle variabili quando utilizzate un interprete persistente.
Negli esempi precedenti abbiamo usato variabili globali nel
package di default Supponiamo che la vostra applicazione sia un server che, di tanto in tanto, eseguirà codice Perl da un file qualunque. Il server non ha modo di sapere quale codice eseguirà. Molto pericoloso. Se il file viene preso da Una possibile strada per evitare collisione nei namespace in questo
scenario consiste nel tradurre il nome del file in un nome di package
che sia sicuramente univoco, e poi compilare il codice all'interno di
questo package utilizzando perlfunc/eval. Nell'esempio di seguito,
ciascun file viene compilato solamente una volta. In alternativa,
l'applicazione potrebbe decidere di ripulire la tabella dei simboli
associata al file una volta che questa non sia più utile. Utilizzando
perlapi/call_argv, chiameremo la funzione
Osservate che il processo continuerà a crescere per ciascun file che
utilizza. In aggiunta, potrebbero esserci subroutine autocaricate
(mediante package Embed::Persistent; #persistente.pl use strict; our %Cache; use Symbol qw(delete_package); sub valid_package_name { my($string) = @_; $string =~ s/([^A-Za-z0-9\/])/sprintf("_%2x",unpack("C",$1))/eg; # seconda passata solo per parole che iniziano con una cifra decimale $string =~ s|/(\d)|sprintf("/_%2x",unpack("C",$1))|eg; # Trasforma in un vero nome di package $string =~ s|/|::|g; return "Embed" . $string; } sub eval_file { my($filename, $delete) = @_; my $package = valid_package_name($filename); my $mtime = -M $filename; if(defined $Cache{$package}{mtime} && $Cache{$package}{mtime} <= $mtime) { # abbiamo compilato questa subroutine almeno una volta, # non ha avuto aggiornamenti su disco, non dobbiamo fare niente print STDERR "$package->handler gia' compilato\n"; } else { local *FH; open FH, $filename or die "open '$filename' $!"; local($/) = undef; my $sub = <FH>; close FH; # Inserisci il codice dentro una subroutine all'interno del nostro # package unico my $eval = qq{package $package; sub handler { $sub; }}; { # hide our variables within this block my($filename,$mtime,$package,$sub); eval $eval; } die $@ if $@; # tieni in cache a meno che non sia ora di pulizie $Cache{$package}{mtime} = $mtime unless $delete; } eval {$package->handler;}; die $@ if $@; delete_package($package) if $delete; # date un'occhiata se volete #print Devel::Symdump->rnew($package)->as_string, $/; } 1; __END__ /* persistente.c */ #include <EXTERN.h> #include <perl.h> /* 1 = ripulisci la tabella del filename dopo ogni richiesa, 0 = non farlo */ #ifndef DO_CLEAN #define DO_CLEAN 0 #endif #define BUFFER_SIZE 1024 static PerlInterpreter *my_perl = NULL; int main(int argc, char **argv, char **env) { char *embedding[] = { "", "persistente.pl" }; char *args[] = { "", DO_CLEAN, NULL }; char filename[BUFFER_SIZE]; int exitstatus = 0; STRLEN n_a; PERL_SYS_INIT3(&argc,&argv,&env); if((my_perl = perl_alloc()) == NULL) { fprintf(stderr, "ho finito la memoria!"); exit(1); } perl_construct(my_perl); exitstatus = perl_parse(my_perl, NULL, 2, embedding, NULL); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; if(!exitstatus) { exitstatus = perl_run(my_perl); while(printf("Nome del file: ") && fgets(filename, BUFFER_SIZE, stdin)) { filename[strlen(filename)-1] = '\0'; /* strip \n */ /* chiama la subroutine, passandole il nome del file */ args[0] = filename; call_argv("Embed::Persistent::eval_file", G_DISCARD | G_EVAL, args); /* check $@ */ if(SvTRUE(ERRSV)) fprintf(stderr, "errore eval: %s\n", SvPV(ERRSV,n_a)); } } PL_perl_destruct_level = 0; perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); exit(exitstatus); } Ora compilate: % cc -o persistente persistente.c `perl -MExtUtils::Embed -e ccopts -e ldopts` Ecco uno script di esempio: #test.pl my $string = "ciao"; foo($string); sub foo { print "foo dice: @_\n"; } Ora lanciate: % persistente Nome del file: test.pl foo dice: ciao Nome del file: test.pl Embed::test_2epl->handler gia' compilato foo dice: ciao Nome del file: ^C
Esecuzione dei blocchi ENDTradizionalmente i blocchi END sono stati pensati per essere eseguiti
alla fine di
Mantenere più istanze dell'interpreteRaramente può succedere che un'applicazione abbia bisogno di creare più di un interprete nel corso di una sessione. Un'applicazione siffatta può sporadicamente decidere di rilasciare qualsiasi risorsa associata con l'interprete. Il programma deve assicurarsi che questo avvenga prima che
l'interprete successivo sia costruito. Per default, quando perl
non viene compilato con opzioni speciali, la variabile globale
Impostando while(1) { ... /* reimposta le variabili globali con PL_perl_destruct_level = 1 */ PL_perl_destruct_level = 1; perl_construct(my_perl); ... /* ripulisci e reimposta _tutto_ durante perl_destruct() */ PL_perl_destruct_level = 1; perl_destruct(my_perl); perl_free(my_perl); ... /* vai, di nuovo! */ } Quando Ora supponete di avere più di un'istanza dell'interprete in corso
allo stesso tempo. È possibile, ma solo se avete utilizzato
l'opzione di configurazione (quando avete lanciato Configure per compilare
perl) Usare Mettiamolo alla prova: #include <EXTERN.h> #include <perl.h> /* integreremo due interpreti */ #define SALUTA "-e", "print qq(Ciao, io sono $^X\n)" int main(int argc, char **argv, char **env) { PerlInterpreter *one_perl, *two_perl; char *one_args[] = { "perl_uno", SALUTA }; char *two_args[] = { "perl_due", SALUTA }; PERL_SYS_INIT3(&argc,&argv,&env); one_perl = perl_alloc(); two_perl = perl_alloc(); PERL_SET_CONTEXT(one_perl); perl_construct(one_perl); PERL_SET_CONTEXT(two_perl); perl_construct(two_perl); PERL_SET_CONTEXT(one_perl); perl_parse(one_perl, NULL, 3, one_args, (char **)NULL); PERL_SET_CONTEXT(two_perl); perl_parse(two_perl, NULL, 3, two_args, (char **)NULL); PERL_SET_CONTEXT(one_perl); perl_run(one_perl); PERL_SET_CONTEXT(two_perl); perl_run(two_perl); PERL_SET_CONTEXT(one_perl); perl_destruct(one_perl); PERL_SET_CONTEXT(two_perl); perl_destruct(two_perl); PERL_SET_CONTEXT(one_perl); perl_free(one_perl); PERL_SET_CONTEXT(two_perl); perl_free(two_perl); PERL_SYS_TERM(); } Notate le chiamate a PERL_SET_CONTEXT(). Queste sono necessarie per inizializzare lo stato globale che tiene traccia di quale interprete è quello ``corrente'' sul particolare processo, o thread, che lo sta eseguendo. Dovrebbe essere sempre utilizzata se avete più di un interprete e state effettuando chiamate all'API perl su entrambi in maniera interlacciata.
Compilate come di consueto: % cc -o molteplicita molteplicita.c `perl -MExtUtils::Embed -e ccopts -e ldopts` Lanciatelo, lanciatelo: % ./molteplicita Ciao, io sono perl_uno Ciao, io sono perl_due
Usare moduli Perl, che usano librerie C, dal vostro programma CSe avete giocato un po' con gli esempi riportati, ed avete provato
ad integrare uno script che utilizza (nel senso di Can't load module Socket, dynamic loading not available in this perl. (You may need to build a new perl executable which either supports dynamic loading or has the Socket module statically linked into it.) [Non posso caricare il modulo Socket, il caricamento dinamico non e' disponibile in questo perl. (Potreste dover compilare un nuovo eseguibile perl che o supporta il caricamento dinamico o ha il modulo Socket linkato staticamente dentro di esso) - N.d.T.] Che cosa è andato storto? Il vostro interprete non sa come comunicare con queste estensioni: un po'
di ``colla'' sarà d'aiuto. Fino ad ora abbiamo chiamato perl_parse(my_perl, NULL, argc, my_argv, NULL); Il codice ``colla'' va inserito proprio qui, nell'interazione iniziale fra Perl e le routine C/C++ collegate. Diamo un'occhiata ad alcune parti di perlmain.c per vedere come lo fa Perl: static void xs_init (pTHX); EXTERN_C void boot_DynaLoader (pTHX_ CV* cv); EXTERN_C void boot_Socket (pTHX_ CV* cv); EXTERN_C void xs_init(pTHX) { char *file = __FILE__; /* DynaLoader e` un caso speciale */ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); newXS("Socket::bootstrap", boot_Socket, file); } Messa in termini semplici: per ciascuna estensione Perl collegata con il
vostro eseguibile (determinata durante la configurazione iniziale sul
vostro computer o quando viene aggiunta una nuova estensione), viene
creata una subroutine Perl per incorporare le routine dell'estensione.
Normalmente, questa routine viene denominata Modulo::bootstrap(), e viene
chiamata quando scrivete Una volta che avete questo codice, inseritelo nel secondo argomento di
perl_parse(my_perl, xs_init, argc, my_argv, NULL); Poi compilate: % cc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts` % interp use Socket; use UnQualcheModuloCaricatoDinamicamente; print "Ora posso utilizzare le estensioni!\n"' ExtUtils::Embed può anche aiutarvi ad automatizzare il processo di
scrittura del codice ``collante'' % perl -MExtUtils::Embed -e xsinit -- -o perlxsi.c % cc -c perlxsi.c `perl -MExtUtils::Embed -e ccopts` % cc -c interp.c `perl -MExtUtils::Embed -e ccopts` % cc -o interp perlxsi.o interp.o `perl -MExtUtils::Embed -e ldopts` Consultate perlxs, perlguts e perlapi per maggiori dettagli.
Integrare Perl in Win32In generale, tutto il codice mostrato qui dovrebbe funzionare senza modifiche sotto Windows. Comunque, ci sono alcune questioni circa gli esempi a linea di comando mostrati che richiedono attenzione. Per cominciare, i backtick (gli apici invertiti, N.d.T.) non funzioneranno nella shell nativa di Win32. Il kit ExtUtils::Embed su CPAN viene fornito con uno script chiamato genmake, che genera un semplice makefile per compilare un programma da un file sorgente C singolo. Può essere utilizzato come segue: C:\ExtUtils-Embed\eg> perl genmake interp.c C:\ExtUtils-Embed\eg> nmake C:\ExtUtils-Embed\eg> interp -e "print qq{Sono integrato in Win32!\n}" Potreste voler usare un ambiente più robusto come Microsoft Developer Studio. In questo caso, lanciate questo comando per generare perlxsi.c: perl -MExtUtils::Embed -e xsinit Crate allora un nuovo progetto ed effettuate
Nascondere Perl_Se volete nascondere completamente le forme brevi delle funzioni dell'API pubblica di Perl, aggiungete -DPERL_NO_SHORT_NAMES alle opzioni di compilazione. Questo significa che nel vostro codice, invece di scrivere: warn("%d bottiglie di birra sul muro", nbottiglie); dovrete scrivere la forma estesa esplicita: Perl_warn(aTHX_ "%d bottiglie di birra sul muro", nbottiglie); (Vedere perlguts/Background and PERL_IMPLICIT_CONTEXT per la
spiegazione di
MORALEPotete a volte scrivere codice più veloce in C, ma potete sempre scrivere più velocemente codice in Perl. Poiché potete utilizzare l'uno dall'altro, combinateli a vostro piacimento.
AUTOREJon Orwant <orwant@media.mit.edu> e Doug MacEachern <dougm@covalent.net>, con alcuni contributi da Tim Bunce, Tom Christiansen, Guy Decoux, Hallvard Furuseth, Dov Grobgeld e Ilya Zakharevich. Doug MacEachern ha un articolo sull'integrazione nel Volume 1, sezione 4 della e-zine The Perl Journal ( http://www.tpj.com/ ). Doug è anche lo sviluppatore dell'integrazione Perl più largamente utilizzata: il sistema mod_perl (perl.apache.org), che integra Perl nel server web Apache. Oracle, Binary Evolution, ActiveState, e la nsapi_perl di Ben Sugars hanno utilizzato questo modello per i plugin Perl di Oracle, Netscape e Internet Information Server.
COPYRIGHTCopyright (C) 1995, 1996, 1997, 1998 Doug MacEachern e Jon Orwant. Tutti i diritti sono riservati. Viene dato permesso di fare e distribuire copie conformi di questa documentazione a patto che la nota di copyright e questa nota di permesso sono preservate su tutte le copie. Viene dato permesso di copiare e distribuire versioni modificate di questa documentazione sotto le condizioni per le copie conformi, a patto che queste sono indicate chiaramente come versioni modificate, che i nomi degli autori ed i titoli rimangono invariati (sebbene i sottotitoli ed i nomi di altri autori possano essere aggiunti), e che l'intero lavoro risultante sia distribuito sotto i termini di una nota di permesso identica alla presente. Viene dato permesso di copiare e distribuire traduzioni di questa documentazione in altre lingue, soggette alle condizioni suddette per le versioni modificate. Si riporta comunque la versione originale inglese della nota di Copyright. Copyright (C) 1995, 1996, 1997, 1998 Doug MacEachern and Jon Orwant. All Rights Reserved. Permission is granted to make and distribute verbatim copies of this documentation provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this documentation under the conditions for verbatim copying, provided also that they are marked clearly as modified versions, that the authors' names and title are unchanged (though subtitles and additional authors' names may be added), and that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this documentation into another language, under the above conditions for modified versions.
TRADUZIONE
VersioneLa versione su cui si basa questa traduzione è ottenibile con: perl -MPOD2::IT -e print_pod perlembed Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
TraduttoreTraduzione a cura di Flavio Poletti.
RevisoreRevisione a cura di dree. Mon Jun 11 22:02:14 2012 |