Aceasta este comanda perlcall care poate fi rulată în furnizorul de găzduire gratuit OnWorks folosind una dintre multiplele noastre stații de lucru online gratuite, cum ar fi Ubuntu Online, Fedora Online, emulator online Windows sau emulator online MAC OS
PROGRAM:
NUME
perlcall - convențiile de apelare Perl din C
DESCRIERE
Scopul acestui document este să vă arate cum să apelați subrutinele Perl direct din C,
adică cum se scrie apeluri de apel.
În afară de a discuta despre interfața C furnizată de Perl pentru scrierea de apeluri inverse documentului
folosește o serie de exemple pentru a arăta cum funcționează de fapt interfața în practică. În
În plus, sunt acoperite unele tehnici de codificare a apelurilor inverse.
Exemplele în care sunt necesare apeluri inverse includ
· Un manipulator de erori
Ați creat o interfață XSUB pentru API-ul C al unei aplicații.
O caracteristică destul de comună în aplicații este aceea de a vă permite să definiți o funcție C care
va fi apelat ori de câte ori se întâmplă ceva urât. Ceea ce ne-am dori este să putem
specificați o subrutină Perl care va fi apelată în schimb.
· Un program bazat pe evenimente
Exemplul clasic de utilizare a apelurilor inverse este atunci când scrieți un eveniment determinat
program, cum ar fi pentru o aplicație X11. În acest caz, înregistrați funcții pentru a fi
apelat ori de câte ori apar evenimente specifice, de exemplu, un buton al mouse-ului este apăsat, cursorul
se mută într-o fereastră sau este selectat un element de meniu.
Deși tehnicile descrise aici sunt aplicabile la încorporarea Perl într-un program C,
acesta nu este scopul principal al acestui document. Sunt și alte detalii care trebuie să fie
luate în considerare și sunt specifice pentru încorporarea Perl. Pentru detalii despre încorporarea Perl în C, consultați
perlembed.
Înainte de a vă lansa cu capul întâi în restul acestui document, ar fi bine
idee să fi citit următoarele două documente - perlxs și perlguts.
THE APEL_ FUNCȚII
Deși aceste lucruri sunt mai ușor de explicat folosind exemple, mai întâi trebuie să fii conștient de câteva
definiții importante.
Perl are o serie de funcții C care vă permit să apelați subrutinele Perl. Sunt
I32 call_sv(SV* sv, steaguri I32);
I32 call_pv(char *subname, I32 flags);
I32 call_method(char *methname, I32 flags);
I32 call_argv(char *subname, I32 flags, char **argv);
Funcția cheie este call_sv. Toate celelalte funcții sunt ambalaje destul de simple care
ușurează apelarea subrutinelor Perl în cazuri speciale. La sfârșitul zilei o vor face
toate apelează call_sv pentru a invoca subrutina Perl.
Toate apel_* funcțiile au un parametru „steaguri” care este folosit pentru a trece o mască de biți de
opțiuni pentru Perl. Această mască de biți funcționează identic pentru fiecare dintre funcții. The
setările disponibile în masca de biți sunt discutate în „VALORI STRAP”.
Fiecare dintre funcții va fi acum discutată pe rând.
call_sv
call_sv ia doi parametri. Primul, „sv”, este un SV*. Acest lucru vă permite să specificați
subrutina Perl să fie apelată fie ca șir C (care a fost mai întâi convertit
la un SV) sau o referire la o subrutină. Sectiunea, Utilizarea call_sv, arată cum tu
poate folosi call_sv.
call_pv
Functia, call_pv, este similar cu call_sv cu excepția faptului că se așteaptă ca primul său parametru
să fie un caracter C* care identifică subrutina Perl pe care doriți să o apelați, de exemplu,
„call_pv(„fred”, 0)”. Dacă subrutina pe care doriți să o apelați este într-un alt pachet, doar
includeți numele pachetului în șir, de exemplu, „pkg::fred”.
call_method
Funcția call_method este folosit pentru a apela o metodă dintr-o clasă Perl. Parametrul
„methname” corespunde numelui metodei de apelat. Rețineți că clasa
căreia îi aparține metoda este transmisă mai degrabă în stiva Perl decât în parametru
listă. Această clasă poate fi fie numele clasei (pentru o metodă statică), fie a
referire la un obiect (pentru o metodă virtuală). Consultați perlobj pentru mai multe informații despre
metode statice și virtuale și „Utilizarea call_method” pentru un exemplu de utilizare
call_method.
call_argv
call_argv apelează subrutina Perl specificată de șirul C stocat în „subnume”
parametru. De asemenea, ia parametrul obișnuit „steaguri”. Parametrul final, „argv”,
constă dintr-o listă terminată cu NULL de șiruri C care urmează să fie transmise ca parametri către
Subrutină Perl. Vedea Utilizarea call_argv.
Toate funcțiile returnează un număr întreg. Acesta este o contorizare a numărului de articole returnate de
subrutina Perl. Elementele reale returnate de subrutină sunt stocate pe Perl
grămadă.
Ca regulă generală ar trebui mereu verificați valoarea returnată de la aceste funcții. Chiar dacă
vă așteptați ca doar un anumit număr de valori să fie returnat de la Perl
subrutină, nu există nimic care să împiedice pe cineva să facă ceva neașteptat - nu spune
nu ai fost avertizat.
FLAG VALORI
Parametrul „steaguri” în toate apel_* funcțiile este una dintre G_VOID, G_SCALAR sau G_ARRAY,
care indică contextul apelului, SAU împreună cu o mască de biți a oricărei combinații a
alte simboluri G_* definite mai jos.
G_VOID
Apelează subrutina Perl într-un context nul.
Acest steag are 2 efecte:
1. Indică subrutinei apelate că se execută într-un context nul
(dacă se execută wantarray rezultatul va fi valoarea nedefinită).
2. Se asigură că nimic nu este returnat efectiv din subrutină.
Valoarea returnată de apel_* funcția indică câte articole au fost returnate de
subrutina Perl - în acest caz va fi 0.
G_SCALAR
Apelează subrutina Perl într-un context scalar. Aceasta este setarea implicită a semnalizatorului de context
pentru toate apel_* funcții.
Acest steag are 2 efecte:
1. Indică subrutinei apelate că se execută într-un context scalar
(dacă se execută wantarray rezultatul va fi fals).
2. Se asigură că numai un scalar este returnat efectiv din subrutină. The
subrutina poate, desigur, ignora wantarray și returnează o listă oricum. Daca da,
atunci va fi returnat doar ultimul element al listei.
Valoarea returnată de apel_* funcția indică câte articole au fost returnate de
subrutina Perl - în acest caz va fi fie 0, fie 1.
Dacă 0, atunci ați specificat indicatorul G_DISCARD.
Dacă este 1, atunci articolul returnat efectiv de subrutina Perl va fi stocat pe Perl
stivă - secțiunea Revenind a mărime scalară arată cum să accesați această valoare pe stivă.
Amintiți-vă că, indiferent de câte elemente returnează subrutina Perl, doar ultimul
va fi accesibil din stivă - gândiți-vă la cazul în care o singură valoare este returnată ca
fiind o listă cu un singur element. Orice alte articole care au fost returnate nu vor exista de către
controlul timpului revine de la apel_* funcţie. Sectiunea Revenind a listă in a
mărime scalară context arată un exemplu al acestui comportament.
G_ARRAY
Apelează subrutina Perl într-un context de listă.
Ca și în cazul G_SCALAR, acest steag are 2 efecte:
1. Indică subrutinei apelate că se execută într-un context de listă
(dacă se execută wantarray rezultatul va fi adevărat).
2. Se asigură că toate articolele returnate din subrutină vor fi accesibile când
controlul se întoarce de la apel_* Funcția.
Valoarea returnată de apel_* funcția indică câte articole au fost returnate de
subrutina Perl.
Dacă 0, atunci ați specificat indicatorul G_DISCARD.
Dacă nu este 0, atunci va fi o contorizare a numărului de articole returnate de subrutină. Aceste
articolele vor fi stocate în stiva Perl. Sectiunea Revenind a listă of Valorile dă o
exemplu de utilizare a steagului G_ARRAY și a mecanismelor de accesare a articolelor returnate din
stiva Perl.
G_SCART
Implicit, apel_* funcțiile plasează elementele returnate de subrutina Perl
teancul. Dacă nu sunteți interesat de aceste articole, atunci setarea acestui steag va face
Perl scăpa de ele automat pentru tine. Rețineți că este încă posibil să indicați a
context la subrutina Perl folosind fie G_SCALAR, fie G_ARRAY.
Dacă nu setați acest steag, atunci este foarte important să vă asigurați că orice
temporare (adică parametrii trecuți subrutinei Perl și valorile returnate de la
subrutine) sunt eliminate de tine. Sectiunea Revenind a mărime scalară oferă detalii despre cum
să dispună de aceste temporare în mod explicit și secțiunea Utilizarea Perl la are of
temporare discută circumstanțele specifice în care puteți ignora problema și lăsați
Perl se ocupă de asta pentru tine.
G_NOARGS
Ori de câte ori o subrutină Perl este apelată folosind unul dintre apel_* funcții, este asumat de
implicit, parametrii trebuie să fie transferați subrutinei. Dacă nu treci de niciuna
parametrii subrutinei Perl, puteți economisi puțin timp setând acest flag. Aceasta
are efectul de a nu crea matricea @_ pentru subrutina Perl.
Deși funcționalitatea oferită de acest indicator poate părea simplă, ar trebui să fie
folosit numai dacă există un motiv întemeiat pentru a face acest lucru. Motivul pentru a fi precaut este că, chiar
dacă ați specificat indicatorul G_NOARGS, este încă posibil pentru subrutina Perl asta
a fost chemat să creadă că i-ați trecut parametrii.
De fapt, ceea ce se poate întâmpla este ca subrutina Perl pe care ați apelat-o să poată accesa @_
matrice dintr-o subrutină Perl anterioară. Acest lucru se va întâmpla atunci când codul care se execută
il apel_* funcția a fost apelată de la o altă subrutină Perl. Codul de mai jos
ilustrează acest lucru
sub fred
{ imprimați „@_\n” }
sub joe
{ &fred }
&joe(1,2,3);
Aceasta se va imprima
1 2 3
Ceea ce s-a întâmplat este că „fred” accesează matricea @_ care îi aparține lui „joe”.
G_EVAL
Este posibil ca subrutina Perl pe care o apelați să se termine anormal, de exemplu, prin
apel muri explicit sau neexistând efectiv. În mod implicit, când oricare dintre acestea
evenimente, procesul se va încheia imediat. Dacă vrei să prinzi acest tip de
eveniment, specificați steag-ul G_EVAL. Va pune o eval { } în jurul apelului subrutinei.
Ori de câte ori controlul revine de la apel_* trebuie să verificați variabila $@ ca și dvs
ar fi într-un script Perl normal.
Valoarea returnată de la apel_* funcția depinde de ce au fost alte steaguri
specificat și dacă a apărut o eroare. Iată toate cazurile diferite care pot
apar:
· Dacă apel_* funcția revine normal, apoi valoarea returnată este cea specificată în
secțiunile anterioare.
· Dacă este specificat G_DISCARD, valoarea returnată va fi întotdeauna 0.
· Dacă este specificat G_ARRAY și a apărut o eroare, valoarea returnată va fi întotdeauna 0.
· Dacă este specificat G_SCALAR și a apărut o eroare, valoarea returnată va fi 1 și
valoarea din partea de sus a stivei va fi undef. Aceasta înseamnă că dacă ai făcut deja
a detectat eroarea bifând $@ și doriți ca programul să continue, trebuie
amintiți-vă să pop undef din stivă.
Vedea Utilizarea G_EVAL pentru detalii despre utilizarea G_EVAL.
G_KEEPERR
Folosirea steagului G_EVAL descris mai sus va seta întotdeauna $@: ștergerea lui dacă nu a existat
eroare și setându-l să descrie eroarea dacă a existat o eroare în codul apelat.
Aceasta este ceea ce îți dorești dacă intenția ta este să gestionezi eventualele erori, dar uneori tu
Vreau doar să capteze erorile și să le împiedice să interfereze cu restul programului.
Acest scenariu va fi aplicabil în mare parte codului care este menit să fie apelat din interior
destructori, apeluri inverse asincrone și gestionare de semnal. În astfel de situații, în care
codul apelat are puțină legătură cu contextul dinamic din jur, programul principal
trebuie izolat de erorile din codul apelat, chiar dacă acestea nu pot fi gestionate
inteligent. De asemenea, poate fi util să faceți acest lucru cu codul pentru „__DIE__” sau „__WARN__”
cârlige și funcții de „legare”.
Indicatorul G_KEEPERR este menit să fie utilizat împreună cu G_EVAL în apel_* funcţii care
sunt folosite pentru a implementa un astfel de cod sau cu „eval_sv”. Acest steag nu are niciun efect asupra
„call_*” funcţionează când G_EVAL nu este utilizat.
Când este utilizat G_KEEPERR, orice eroare în codul apelat va termina apelul ca de obicei și
eroarea nu se va propaga dincolo de apel (ca de obicei pentru G_EVAL), dar nu va merge
în $@. În schimb, eroarea va fi convertită într-un avertisment, prefixat cu șirul
„\t(în curățare)”. Aceasta poate fi dezactivată folosind „fără avertismente „diverse””. Dacă nu există nicio eroare,
$@ nu va fi șters.
Rețineți că indicatorul G_KEEPERR nu se propagă în evaluările interioare; acestea pot seta în continuare $@.
Indicatorul G_KEEPERR a fost introdus în versiunea Perl 5.002.
Vedea Utilizarea G_KEEPERR pentru un exemplu de situație care justifică utilizarea acestui steag.
Determinarea il Context
După cum sa menționat mai sus, puteți determina contextul subrutinei care se execută în prezent
Perl cu wantarray. Testul echivalent poate fi făcut în C folosind macro-ul „GIMME_V”,
care returnează „G_ARRAY” dacă ați fost apelat într-un context de listă, „G_SCALAR” dacă într-un
context scalar sau „G_VOID” dacă este într-un context nul (adică, valoarea returnată nu va fi
folosit). O versiune mai veche a acestei macrocomenzi se numește „GIMME”; într-un context de vid se întoarce
„G_SCALAR” în loc de „G_VOID”. Un exemplu de utilizare a macrocomenzii „GIMME_V” este prezentat în
secțiune Utilizarea GIMME_V.
EXEMPLE
Ajunge cu definiția vorbită! Să avem câteva exemple.
Perl oferă multe macrocomenzi pentru a ajuta la accesarea stivei Perl. Ori de câte ori este posibil, acestea
macrocomenzile ar trebui să fie întotdeauna folosite atunci când interfațați cu elementele interne Perl. Sperăm că acest lucru ar trebui să facă
codul mai puțin vulnerabil la orice modificări aduse Perl în viitor.
Un alt punct demn de remarcat este că în prima serie de exemple am folosit doar
il call_pv funcţie. Acest lucru a fost făcut pentru a menține codul mai simplu și pentru a vă facilita accesul
subiect. Ori de câte ori este posibil, dacă alegerea este între utilizarea call_pv și call_sv, Ar trebui
încercați întotdeauna să utilizați call_sv. Vedea Utilizarea call_sv pentru detalii.
Nu Parametri, Nimic Întors
Acest prim exemplu banal va numi o subrutină Perl, PrintUID, pentru a tipări UID-ul
procesul.
sub PrintUID
{
printează „UID-ul este $<\n”;
}
și aici este o funcție C pentru a o numi
gol static
call_PrintUID()
{
dSP;
PUSHMARK(SP);
call_pv("PrintUID", G_DISCARD|G_NOARGS);
}
Simplu, nu?
Câteva puncte de remarcat despre acest exemplu:
1. Ignorați „dSP” și „PUSHMARK(SP)” pentru moment. Ele vor fi discutate în exemplul următor.
2. Nu îi transmitem niciun parametru PrintUID deci G_NOARGS poate fi specificat.
3. Nu ne interesează nimic din care se returnează PrintUID, deci G_DISCARD este specificat.
Chiar dacă PrintUID a fost modificat pentru a returna unele valori, după ce G_DISCARD va fi specificat
înseamnă că vor fi șterse de controlul de timp se întoarce de la call_pv.
4. Ca call_pv este utilizat, subrutina Perl este specificată ca șir C. In acest
în cazul în care numele subrutinei a fost „conectat” în cod.
5. Deoarece am specificat G_DISCARD, nu este necesar să verificăm valoarea returnată de la
call_pv. Va fi mereu 0.
Care trece parametrii
Acum să facem un exemplu puțin mai complex. De data aceasta vrem să numim un Perl
subrutina, "LeftString", care va lua 2 parametri - un șir ($s) și un întreg ($n).
Subrutina va imprima pur și simplu primele $n caractere ale șirului.
Deci subrutina Perl ar arăta astfel:
sub LeftString
{
meu($s, $n) = @_;
print substr($s, 0, $n), „\n”;
}
Funcția C necesară pentru a apela LeftString ar arata asa:
gol static
call_LeftString(a, b)
char * a;
int b;
{
dSP;
INTRODUCE;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(a, 0)));
XPUSHs(sv_2mortal(newSViv(b)));
PUNE INAPOI;
call_pv("LeftString", G_DISCARD);
FREETMPS;
PĂRĂSI;
}
Iată câteva note despre funcția C call_LeftString.
1. Parametrii sunt transferați subrutinei Perl folosind stiva Perl. Acesta este
scopul codului începând cu linia „dSP” și terminând cu linia „PUTBACK”.
„dSP” declară o copie locală a indicatorului de stivă. Această copie locală ar trebui mereu
fi accesat ca „SP”.
2. Dacă aveți de gând să puneți ceva în stiva Perl, trebuie să știți unde să puneți
aceasta. Acesta este scopul macro-ului „dSP” - declară și inițializează a local copiaţi
a indicatorului de stivă Perl.
Toate celelalte macrocomenzi care vor fi utilizate în acest exemplu necesită să fi folosit acest lucru
macro.
Excepția de la această regulă este dacă apelați o subrutină Perl direct dintr-un
Funcția XSUB. În acest caz, nu este necesar să utilizați macro-ul „dSP” în mod explicit – acesta
va fi declarat automat pentru dvs.
3. Orice parametri care trebuie împins pe stivă ar trebui să fie între paranteze de „PUSHMARK” și
Macrocomenzi „PUTBACK”. Scopul acestor două macrocomenzi, în acest context, este de a număra
numărul de parametri pe care îi apăsați automat. Apoi, ori de câte ori Perl creează
matricea @_ pentru subrutină, știe cât de mare să o facă.
Macro-ul „PUSHMARK” îi spune lui Perl să facă o notă mentală a indicatorului de stivă curent.
Chiar dacă nu treceți niciun parametru (cum ar fi exemplul prezentat în secțiunea Nu
Parametri, Nimic Întors) trebuie să apelați în continuare macrocomandă „PUSHMARK” înainte de a putea
sunați la oricare dintre apel_* funcții--Perl încă trebuie să știe că nu există
parametri.
Macro-ul „PUTBACK” setează copia globală a indicatorului stivei să fie aceeași cu a noastră
copie locală. Dacă nu am făcut asta, call_pv nu ar ști unde suntem cei doi parametri
au fost împinsi--amintiți-vă că până acum toată manipularea pointer-ului stivei pe care am făcut-o
este cu copia noastră locală, nu copia globală.
4. În continuare, ajungem la XPUSH-uri. Aici parametrii sunt de fapt împinși pe
grămadă. În acest caz, împingem un șir și un întreg.
Consultați „XSUB-urile și stiva de argumente” în perlguts pentru detalii despre modul în care macrocomenzile XPUSH
muncă.
5. Pentru că am creat valori temporare (prin sv_2muritor() apeluri) va trebui
faceți ordine în stiva Perl și aruncați SV-urile muritoare.
Acesta este scopul
INTRODUCE;
SAVETMPS;
la începutul funcției și
FREETMPS;
PĂRĂSI;
la sfarsit. Perechea „ENTER”/“SAVETMPS” creează o limită pentru orice temporar noi
crea. Asta înseamnă că temporarele de care scăpăm se vor limita la cele care
au fost create după aceste apeluri.
Perechea „FREETMPS”/“LEAVE” va scăpa de orice valoare returnată de Perl
subrutină (a se vedea exemplul următor), plus că va arunca și SV-urile muritoare pe care le-am creat.
Dacă aveți „ENTER”/”SAVETMPS” la începutul codului, vă asigurați că nu există altul
muritorii sunt distruși.
Gândiți-vă la aceste macrocomenzi ca funcționând un pic ca „{” și „}” în Perl pentru a limita domeniul de aplicare
variabile locale.
Vezi secțiunea Utilizarea Perl la are of Temporarii pentru detalii despre o alternativă la
folosind aceste macrocomenzi.
6. În cele din urmă, LeftString poate fi apelat acum prin intermediul call_pv funcţie. Singurul steag
specificată această dată este G_DISCARD. Pentru că trecem 2 parametri la Perl
de data aceasta, nu am specificat G_NOARGS.
Revenind a mărime scalară
Acum, pentru un exemplu de tratare cu elementele returnate dintr-o subrutină Perl.
Iată o subrutină Perl, Sumator, care ia 2 parametri întregi și pur și simplu returnează lor
sumă.
sub Adder
{
meu($a, $b) = @_;
$a + $b;
}
Pentru că acum suntem preocupați de valoarea returnată de la Sumator, funcția C necesară
numiți acum este puțin mai complex.
gol static
call_Adder(a, b)
int a;
int b;
{
dSP;
număr de int;
INTRODUCE;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
PUNE INAPOI;
count = call_pv("Adder", G_SCALAR);
SPANIA;
dacă (număr != 1)
croak(„Necazuri mari\n”);
printf ("Suma %d și %d este %d\n", a, b, POPi);
PUNE INAPOI;
FREETMPS;
PĂRĂSI;
}
Puncte de remarcat de data aceasta sunt
1. Singurul flag specificat de data aceasta a fost G_SCALAR. Asta înseamnă că matricea @_ va fi
creat și că valoarea returnată de Sumator va exista în continuare după apelul către
call_pv.
2. Scopul macro-ului „SPAGAIN” este de a reîmprospăta copia locală a pointerului stivei.
Acest lucru este necesar deoarece este posibil ca memoria alocată stivei Perl
a fost realocat în timpul call_pv apel.
Dacă utilizați indicatorul de stivă Perl în codul dvs., trebuie să vă reîmprospătați întotdeauna
copia locală folosind SPAGAIN ori de câte ori utilizați apel_* funcții sau oricare
altă funcție internă Perl.
3. Deși se aștepta să fie returnată doar o singură valoare de la Sumator, tot e bine
practică pentru a verifica codul de retur de la call_pv oricum.
A aștepta o singură valoare nu este chiar același lucru cu a ști că va exista una. Dacă
cineva modificat Sumator să returnăm o listă și nu am verificat acea posibilitate și
luați măsurile adecvate, stiva Perl ar ajunge într-o stare inconsistentă. Acesta este
ceva tu într-adevăr nu vreau să se întâmple niciodată.
4. Macro-ul „POPi” este folosit aici pentru a scoate valoarea returnată din stivă. În acest caz
am vrut un număr întreg, așa că a fost folosit „POPi”.
Iată lista completă a macrocomenzilor POP disponibile, împreună cu tipurile pe care le returnează.
POPs SV
Pointer POPp
POPn dublu
POPi întreg
POPl lung
5. „PUTBACK” final este folosit pentru a lăsa stiva Perl într-o stare consecventă înainte
ieșirea din funcție. Acest lucru este necesar deoarece atunci când am scos valoarea returnată de la
stiva cu „POPi” a actualizat doar copia noastră locală a indicatorului de stivă. Tine minte,
„PUTBACK” setează indicatorul de stivă globală să fie același cu copia noastră locală.
Revenind a Listă of Valori
Acum, să extindem exemplul anterior pentru a returna atât suma parametrilor, cât și valoarea
diferență.
Aici este subrutina Perl
sub AddSubtract
{
meu($a, $b) = @_;
($a+$b, $a-$b);
}
și aceasta este funcția C
gol static
call_AddSubtract(a, b)
int a;
int b;
{
dSP;
număr de int;
INTRODUCE;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
PUNE INAPOI;
count = call_pv("AddSubtract", G_ARRAY);
SPANIA;
dacă (număr != 2)
croak(„Necazuri mari\n”);
printf ("%d - %d = %d\n", a, b, POPi);
printf ("%d + %d = %d\n", a, b, POPi);
PUNE INAPOI;
FREETMPS;
PĂRĂSI;
}
If call_AddSubtract se numeste asa
call_AddSubtract(7, 4);
atunci aici este rezultatul
7 - 4 = 3
7 + 4 = 11
notițe
1. Am vrut contextul listei, așa că a fost folosit G_ARRAY.
2. Nu este surprinzător că „POPi” este folosit de două ori de data aceasta deoarece am preluat 2 valori
din stivă. Lucrul important de remarcat este că atunci când utilizați macrocomenzile „POP*”, acestea
ieși din stivă înăuntru inversa comandă.
Revenind a Listă in a mărime scalară Context
Să presupunem că subrutina Perl din secțiunea anterioară a fost numită într-un context scalar, ca acesta
gol static
call_AddSubScalar(a, b)
int a;
int b;
{
dSP;
număr de int;
int i;
INTRODUCE;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
PUNE INAPOI;
count = call_pv("AddSubtract", G_SCALAR);
SPANIA;
printf ("Elemente returnate = %d\n", count);
pentru (i = 1; i <= numără; ++i)
printf ("Valoarea %d = %d\n", i, POPi);
PUNE INAPOI;
FREETMPS;
PĂRĂSI;
}
Cealaltă modificare făcută este aceea call_AddSubScalar va imprima numărul de articole
returnate din subrutina Perl și valoarea lor (pentru simplitate se presupune că sunt
întreg). Astfel, dacă call_AddSubScalar se numește
call_AddSubScalar(7, 4);
atunci ieșirea va fi
Articole returnate = 1
Valoarea 1 = 3
În acest caz, principalul punct de reținut este că numai ultimul element din listă este returnat
din subrutină. AdaugăReducere de fapt s-a întors la call_AddSubScalar.
Revenind Date din Perl de il Parametru Listă
De asemenea, este posibil să returnați valori direct prin lista de parametri, indiferent dacă este
de fapt, de dorit să o facă este cu totul altă chestiune.
Subrutina Perl, Inc, mai jos ia 2 parametri și crește fiecare direct.
sub Inc
{
++ $_[0];
++ $_[1];
}
și aici este o funcție C pentru a o numi.
gol static
call_Inc(a, b)
int a;
int b;
{
dSP;
număr de int;
SV * sva;
SV * svb;
INTRODUCE;
SAVETMPS;
sva = sv_2mortal(nouSViv(a));
svb = sv_2mortal(nouSViv(b));
PUSHMARK(SP);
XPUSH-uri (sva);
XPUSH-uri (svb);
PUNE INAPOI;
count = call_pv("Inc", G_DISCARD);
dacă (număr != 0)
croak ("call_Inc: așteptate 0 valori de la „Inc”, a primit %d\n",
numara);
printf ("%d + 1 = %d\n", a, SvIV(sva));
printf ("%d + 1 = %d\n", b, SvIV(svb));
FREETMPS;
PĂRĂSI;
}
Pentru a putea accesa cei doi parametri care au fost împinși în stivă după ce au revenit
din call_pv este necesar să se noteze adresele lor – deci cele două variabile
„sva” și „svb”.
Motivul pentru care acest lucru este necesar este că zona stivei Perl care le ține va fi foarte
probabil că au fost suprascrise de altceva de către controlul timpului de la care se întoarce call_pv.
Utilizarea G_EVAL
Acum un exemplu folosind G_EVAL. Mai jos este o subrutină Perl care calculează diferența dintre
cei 2 parametri ai săi. Dacă acest lucru ar avea ca rezultat un rezultat negativ, subrutinele apelează muri.
scădere Scădere
{
meu ($a, $b) = @_;
die „moartea poate fi fatală\n” dacă $a < $b;
$a - $b;
}
și niște C să-i spună
gol static
call_Subtract(a, b)
int a;
int b;
{
dSP;
număr de int;
INTRODUCE;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
PUNE INAPOI;
count = call_pv("Scădere", G_EVAL|G_SCALAR);
SPANIA;
/* Verificați mai întâi evalul */
dacă (SvTRUE(ERRSV))
{
printf ("Uh oh - %s\n", SvPV_nolen(ERRSV));
POP-uri;
}
altfel
{
dacă (număr != 1)
croak("call_Subtract: am dorit 1 valoare din 'Subtract', am primit %d\n",
numara);
printf ("%d - %d = %d\n", a, b, POPi);
}
PUNE INAPOI;
FREETMPS;
PĂRĂSI;
}
If call_Subtract se numeste astfel
call_Subtract(4, 5)
vor fi tipărite următoarele
Uh oh - moartea poate fi fatală
notițe
1. Vrem să putem prinde muri deci am folosit steag-ul G_EVAL. Nespecificând
acest steag ar însemna că programul se va încheia imediat la muri
declarație din subrutină Scădea.
2. Codul
dacă (SvTRUE(ERRSV))
{
printf ("Uh oh - %s\n", SvPV_nolen(ERRSV));
POP-uri;
}
este echivalentul direct al acestui bit de Perl
imprimă „Uh oh - $@\n” dacă $@;
„PL_errgv” este un perl global de tip „GV *” care indică intrarea tabelului de simboluri
conţinând eroarea. Prin urmare, „ERRSV” se referă la echivalentul C al lui $@.
3. Rețineți că stiva este deschisă folosind „POP-uri” în blocul în care este „SvTRUE(ERRSV)”
Adevărat. Acest lucru este necesar deoarece ori de câte ori a apel_* funcţie invocată cu
G_EVAL|G_SCALAR returnează o eroare, partea de sus a stivei păstrează valoarea undef. pentru că
dorim ca programul să continue după detectarea acestei erori, este esențial ca
stiva fie aranjată prin îndepărtarea undef.
Utilizarea G_KEEPERR
Luați în considerare acest exemplu destul de fațios, în care am folosit o versiune XS a
call_Subtract exemplu de mai sus în interiorul unui destructor:
pachet Foo;
sub nou { binecuvântează {}, $_[0] }
scădea {
meu($a,$b) = @_;
die „moartea poate fi fatală” dacă $a < $b;
$a - $b;
}
sub DISTROY { call_Subtract(5, 4); }
sub foo { die „foo dies”; }
pachet principal;
{
my $foo = Foo->new;
eval { $foo->foo };
}
tipăriți „Saw: $@” dacă $@; # ar trebui să fie, dar nu este
Acest exemplu nu va recunoaște că a apărut o eroare în „eval {}”. Iată
de ce: codul call_Subtract a fost executat în timp ce perl curăța temporar când
ieșirea din blocul exterior contravântuit și pentru că call_Subtract este implementat cu call_pv
folosind indicatorul G_EVAL, resetează imediat $@. Acest lucru are ca rezultat eșecul celui mai exterior
testați pentru $@ și, prin urmare, eșecul capcanei de eroare.
Adăugarea indicatorului G_KEEPERR, astfel încât call_pv call in call_Subtract citește:
count = call_pv("Scădere", G_EVAL|G_SCALAR|G_KEEPERR);
va păstra eroarea și va restabili gestionarea fiabilă a erorilor.
Utilizarea call_sv
În toate exemplele anterioare am „cablat” numele subrutinei Perl
apelat de la C. De cele mai multe ori, totuși, este mai convenabil să poți specifica
numele subrutinei Perl din scriptul Perl.
Luați în considerare codul Perl de mai jos
sub fred
{
printează „Bună ziua\n”;
}
CallSubPV ("fred");
Iată un fragment din XSUB care definește CallSubPV.
anula
CallSubPV(nume)
char * nume
COD:
PUSHMARK(SP);
call_pv(nume, G_DISCARD|G_NOARGS);
E bine în măsura în care merge. Chestia este că subrutina Perl poate fi specificată numai ca
un șir, totuși, Perl permite referințe la subrutine și subrutine anonime. Acest
este unde call_sv e folositor.
Codul de mai jos pentru CallSubSV este identic cu CallSubPV cu excepția faptului că parametrul „nume” este
acum definit ca SV* și folosim call_sv în loc de call_pv.
anula
CallSubSV(nume)
SV * nume
COD:
PUSHMARK(SP);
call_sv(nume, G_DISCARD|G_NOARGS);
Pentru că folosim un SV pentru a apela Pace pot fi folosite toate următoarele:
CallSubSV ("fred");
CallSubSV(\&fred);
$ref = \&fred;
CallSubSV($ref);
CallSubSV( sub { print "Bună ziua\n" } );
După cum puteți vedea, call_sv vă oferă o flexibilitate mult mai mare în modul în care puteți specifica Perl
subrutină.
Trebuie să rețineți că, dacă este necesar să stocați SV ("nume" în exemplul de mai sus)
care corespunde subrutinei Perl astfel încât să poată fi folosit mai târziu în program, it
nu suficient doar pentru a stoca o copie a indicatorului către SV. Să spunem că codul de mai sus a fost ca
acest:
SV static * rememberSub;
anula
SaveSub1(nume)
SV * nume
COD:
rememberSub = nume;
anula
CallSavedSub1()
COD:
PUSHMARK(SP);
call_sv(rememberSub, G_DISCARD|G_NOARGS);
Motivul pentru care acest lucru este greșit este că, în momentul în care ajungeți să utilizați indicatorul „rememberSub” în
„CallSavedSub1”, se poate referi sau nu la subrutina Perl care a fost înregistrată în
„Salvare Sub1”. Acest lucru este valabil mai ales pentru aceste cazuri:
SaveSub1(\&fred);
CallSavedSub1();
SaveSub1( sub { print "Bună ziua\n" } );
CallSavedSub1();
În momentul în care fiecare dintre instrucțiunile „SaveSub1” de mai sus a fost executată, SV*s care
corespunzator parametrilor nu va mai exista. Așteptați-vă un mesaj de eroare de la Perl de
forma
Nu se poate folosi o valoare nedefinită ca referință de subrutină la...
pentru fiecare dintre liniile „CallSavedSub1”.
La fel, cu acest cod
$ref = \&fred;
SaveSub1($ref);
$ref = 47;
CallSavedSub1();
vă puteți aștepta la unul dintre aceste mesaje (pe care îl primiți de fapt depinde de versiune
de Perl pe care îl utilizați)
Nu este o referință la COD la...
Subrutină nedefinită &main::47 numită...
Este posibil ca variabila $ref să se fi referit la subrutina „fred” ori de câte ori apelul la
„SaveSub1” a fost făcut, dar în momentul în care „CallSavedSub1” este apelat, acum deține numărul
47. Deoarece am salvat doar un pointer către SV original în „SaveSub1”, orice modificări aduse $ref
va fi urmărit de indicatorul „rememberSub”. Aceasta înseamnă că ori de câte ori „CallSavedSub1”
este apelat, va încerca să execute codul la care face referire SV*
„rememberSub”. În acest caz, totuși, acum se referă la întregul 47, așa că așteptați-vă ca Perl
plângeți cu voce tare.
O problemă similară, dar mai subtilă este ilustrată cu acest cod:
$ref = \&fred;
SaveSub1($ref);
$ref = \&joe;
CallSavedSub1();
De data aceasta, ori de câte ori „CallSavedSub1” este apelat, acesta va executa subrutina Perl „joe”
(presupunând că există) mai degrabă decât „fred”, așa cum a fost solicitat inițial în apelul către
„Salvare Sub1”.
Pentru a ocoli aceste probleme, este necesar să luați o copie completă a SV. Codul
mai jos arată „SaveSub2” modificat pentru a face asta.
SV static * keepSub = (SV*)NULL;
anula
SaveSub2(nume)
SV * nume
COD:
/* Ia o copie a apelului invers */
if (keepSub == (SV*)NULL)
/* Prima dată, așa că creează un nou SV */
keepSub = newSVsv(nume);
altfel
/* Am mai fost aici, așa că suprascrie */
SvSetSV(keepSub, nume);
anula
CallSavedSub2()
COD:
PUSHMARK(SP);
call_sv(keepSub, G_DISCARD|G_NOARGS);
Pentru a evita crearea unui nou SV de fiecare dată când este apelat „SaveSub2”, funcția verifică mai întâi
vezi daca a mai fost sunat. Dacă nu, atunci spațiul pentru un nou SV este alocat și
referința la subrutina Perl „nume” este copiată în variabila „keepSub” într-unul
operațiune folosind „newsSVsv”. Ulterior, ori de câte ori este apelat „SaveSub2”, SV-ul existent,
„keepSub”, este suprascris cu noua valoare folosind „SvSetSV”.
Utilizarea call_argv
Iată o subrutină Perl care tipărește orice parametri îi sunt trecuți.
sub PrintList
{
mea(@lista) = @_;
foreach (@list) { printează „$_\n” }
}
Și iată un exemplu de call_argv care va chema PrintList.
static char * words[] = {"alpha", "beta", "gamma", "delta", NULL};
gol static
call_PrintList()
{
dSP;
call_argv("PrintList", G_DISCARD, cuvinte);
}
Rețineți că nu este necesar să apelați „PUSHMARK” în acest caz. Asta pentru ca
call_argv o va face pentru tine.
Utilizarea call_method
Luați în considerare următorul cod Perl:
{
pachetul al meu;
sub nou
{
meu($tip) = shift;
binecuvântează [@_]
}
sub display
{
meu ($self, $index) = @_;
printează „$index: $$self[$index]\n”;
}
sub PrintID
{
mea($clasa) = @_;
print „Aceasta este clasa $class versiunea 1.0\n”;
}
}
Implementează doar o clasă foarte simplă pentru a gestiona o matrice. În afară de constructor,
„nou”, declară metode, una statică și una virtuală. Metoda statică, „PrintID”,
imprimă pur și simplu numele clasei și un număr de versiune. Metoda virtuală, „Afișare”,
tipărește un singur element al matricei. Iată un exemplu all-Perl de utilizare.
$a = Mine->new('rosu', 'verde', 'albastru');
$a->Afişa(1);
Mine->PrintID;
va imprima
1: verde
Aceasta este versiunea Class Mine 1.0
Apelarea unei metode Perl din C este destul de simplă. Sunt necesare următoarele lucruri:
· O referire la obiect pentru o metodă virtuală sau numele clasei pentru o metodă statică
metodă
· Numele metodei
· Orice alți parametri specifici metodei
Iată un simplu XSUB care ilustrează mecanica apelării atât „PrintID” cât și
Metodele „Afișează” din C.
anula
call_Method(ref, metodă, index)
SV * ref
metoda char *
index int
COD:
PUSHMARK(SP);
XPUSH-uri (ref);
XPUSHs(sv_2mortal(newSViv(index)));
PUNE INAPOI;
call_method(metoda, G_DISCARD);
anula
call_PrintID(clasa, metoda)
char * clasa
metoda char *
COD:
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(class, 0)));
PUNE INAPOI;
call_method(metoda, G_DISCARD);
Deci metodele „PrintID” și „Display” pot fi invocate astfel:
$a = Mine->new('rosu', 'verde', 'albastru');
call_Method($a, 'Afişare', 1);
call_PrintID('Al meu', 'PrintID');
Singurul lucru de remarcat este că, atât în metodele statice, cât și în cele virtuale, numele metodei este
nu este trecut prin stivă - este folosit ca prim parametru pentru call_method.
Utilizarea GIMME_V
Iată un XSUB banal care tipărește contextul în care se execută în prezent.
anula
PrintContext()
COD:
I32 gimme = GIMME_V;
dacă (da-mi == G_VOID)
printf ("Contextul este nul\n");
else if (da-mi == G_SCALAR)
printf ("Contextul este scalar\n");
altfel
printf ("Contextul este matrice\n");
Și iată niște Perl pentru a-l testa.
PrintContext;
$a = PrintContext;
@a = PrintContext;
Rezultatul din asta va fi
Contextul este Vid
Contextul este scalar
Contextul este Array
Utilizarea Perl la are of Temporarii
În exemplele date până în prezent, orice temporar creat în apel invers (adică, parametrii
a trecut pe stiva la apel_* funcția sau valorile returnate prin stivă) au fost
eliberat prin una dintre aceste metode:
· Specificarea steagului G_DISCARD cu apel_*
· Folosind explicit asocierea „ENTER”/”SAVETMPS”--”FREETMPS”/”LEAVE”
Există o altă metodă care poate fi folosită, și anume lăsați Perl să o facă automat pentru dvs
ori de câte ori își recapătă controlul după încheierea apelului invers. Acest lucru se face pur și simplu nu
folosind
INTRODUCE;
SAVETMPS;
...
FREETMPS;
PĂRĂSI;
secvența în callback (și nu, desigur, specificând steag-ul G_DISCARD).
Dacă intenționați să utilizați această metodă, trebuie să fiți conștient de o posibilă scurgere de memorie care
poate apărea în circumstanțe foarte specifice. Pentru a explica aceste circumstanțe trebuie
cunoașteți puțin despre fluxul de control dintre Perl și rutina de apel invers.
Exemplele date la începutul documentului (un handler de erori și un eveniment condus
program) sunt tipice pentru cele două tipuri principale de control al fluxului pe care probabil să le faceți
întâlnire cu apeluri inverse. Există o distincție foarte importantă între ele, așa că plătiți
Atenţie.
În primul exemplu, un handler de erori, fluxul de control ar putea fi după cum urmează. Aveți
a creat o interfață către o bibliotecă externă. Controlul poate ajunge la biblioteca externă ca
acest
perl --> XSUB --> bibliotecă externă
În timp ce controlul este în bibliotecă, apare o condiție de eroare. Ați configurat anterior un
Perl callback pentru a gestiona această situație, așa că va fi executat. Odată ce apelul înapoi are
terminat, controlul va reveni din nou la Perl. Iată care va fi fluxul de control
ca in situatia aceea
perl --> XSUB --> bibliotecă externă
...
apare o eroare
...
bibliotecă externă --> call_* --> perl
|
perl <-- XSUB <-- bibliotecă externă <-- call_* <----+
După procesarea erorii folosind apel_* este finalizat, controlul revine la Perl mai mult
sau mai putin imediat.
În diagramă, cu cât mergeți mai la dreapta, cu atât scopul este mai adânc imbricat. Este doar
când controlul este din nou cu perl în extrema stângă a diagramei pe care o veți avea
a scăzut înapoi la domeniul de aplicare și orice temporar pe care le-ai lăsat să atârne în jur va fi
fi eliberat.
În al doilea exemplu, un program condus de evenimente, fluxul de control va fi mai degrabă ca acesta
perl --> XSUB --> handler de evenimente
...
handler de evenimente --> call_* --> perl
|
handler de evenimente <-- call_* <----+
...
handler de evenimente --> call_* --> perl
|
handler de evenimente <-- call_* <----+
...
handler de evenimente --> call_* --> perl
|
handler de evenimente <-- call_* <----+
În acest caz, fluxul de control poate consta doar din secvența repetată
handler de evenimente --> call_* --> perl
practic pe toată durata programului. Aceasta înseamnă că controlul poate nu
coborâți înapoi la domeniul înconjurător în Perl la extrema stângă.
Deci, care este marea problemă? Ei bine, dacă vă așteptați ca Perl să facă ordine în acele temporare
pentru tine, s-ar putea să ai o lungă așteptare. Pentru ca Perl să elimine temporar,
controlul trebuie să cadă înapoi la domeniul de aplicare la un moment dat. În scenariul condus de evenimente
asta s-ar putea să nu se întâmple niciodată. Aceasta înseamnă că, pe măsură ce timpul trece, programul tău va crea mai multe
și mai multe temporare, dintre care niciunul nu va fi eliberat vreodată. Ca fiecare dintre aceste temporare
consumă o parte din memorie programul dvs. va consuma în cele din urmă toată memoria disponibilă din dvs
sistem--kapow!
Deci, aici este linia de jos - dacă sunteți sigur că controlul va reveni înapoi la închidere
Scopul Perl destul de repede după încheierea apelului înapoi, atunci nu este absolut
necesare pentru a elimina în mod explicit orice temporare pe care le-ați creat. Ai grijă, dacă tu
nu sunteți deloc siguri despre ce să faceți, oricum nu face niciun rău să faceți ordine.
Strategii pentru depozitarea Callback Context Informații
Potențial una dintre cele mai dificile probleme de depășit atunci când proiectați o interfață de apel invers
poate afla cum să stocheze maparea dintre funcția de apel invers C și Perl
echivalent.
Pentru a vă ajuta să înțelegeți de ce aceasta poate fi o problemă reală, luați în considerare mai întâi modul în care este configurat un apel invers
într-un mediu complet C. De obicei, un API C va oferi o funcție pentru înregistrarea a
suna inapoi. Acest lucru va aștepta un pointer către o funcție ca unul dintre parametrii acesteia. Mai jos este a
apel la o funcție ipotetică „register_fatal” care înregistrează funcția C pentru a obține
apelat atunci când apare o eroare fatală.
register_fatal(cb1);
Singurul parametru „cb1” este un pointer către o funcție, deci trebuie să fi definit „cb1” în
codul tău, spune ceva de genul acesta
gol static
cb1()
{
printf ("Eroare fatală\n");
ieşire(1);
}
Acum schimbați asta pentru a apela în schimb o subrutină Perl
SV static * callback = (SV*)NULL;
gol static
cb1()
{
dSP;
PUSHMARK(SP);
/* Apelați sub Perl pentru a procesa apelul invers */
call_sv(callback, G_DISCARD);
}
anula
register_fatal(fn)
SV * fn
COD:
/* Amintiți-vă de sub Perl */
if (callback == (SV*)NULL)
callback = newSVsv(fn);
altfel
SvSetSV(callback, fn);
/* înregistrează apelul înapoi cu biblioteca externă */
register_fatal(cb1);
unde echivalentul Perl al „register_fatal” și apelul invers pe care îl înregistrează, „pcb1”, ar putea
arata asa
# Înregistrați sub pcb1
register_fatal(\&pcb1);
sub pcb1
{
die „Eu mor...\n”;
}
Maparea dintre callback-ul C și echivalentul Perl este stocată în global
variabila „callback”.
Acest lucru va fi adecvat dacă aveți nevoie vreodată să aveți un singur apel invers înregistrat în orice moment.
Un exemplu ar putea fi un handler de erori precum codul schițat mai sus. Ține minte totuși,
apelurile repetate la „register_fatal” vor înlocui apelul invers înregistrat anterior
functioneaza cu cel nou.
Să spunem, de exemplu, că doriți să interfațați cu o bibliotecă care permite intrare/ieșire a fișierelor asincrone. În
în acest caz, este posibil să puteți înregistra un apel invers ori de câte ori s-a încheiat o operațiune de citire.
Pentru a fi de orice folos, dorim să putem apela subrutine Perl separate pentru fiecare fișier care
este deschis. Așa cum este, exemplul de gestionare a erorilor de mai sus nu ar fi adecvat
permite definirea unui singur apel invers în orice moment. Ceea ce avem nevoie este un mijloc de
stocarea maparii dintre fișierul deschis și subrutina Perl pe care vrem să fim apelați
pentru acel dosar.
Să presupunem că biblioteca i/o are o funcție „asynch_read” care asociază o funcție C
„ProcessRead” cu un mâner de fișier „fh” - aceasta presupune că a furnizat și o rutină
pentru a deschide fișierul și astfel obțineți handle-ul fișierului.
asynch_read(fh, ProcessRead)
Acest lucru se poate aștepta ca C ProcesRead funcţia acestei forme
anula
ProcessRead(fh, buffer)
int fh;
char * buffer;
{
...
}
Pentru a oferi o interfață Perl acestei biblioteci, trebuie să putem mapa între „fh”
parametrul și subrutina Perl pe care vrem să o apelăm. Un hash este un mecanism convenabil pentru
stocarea acestei cartografieri. Codul de mai jos arată o posibilă implementare
static HV * Mapping = (HV*)NULL;
anula
asynch_read(fh, apel invers)
int fh
SV * apel invers
COD:
/* Dacă hash-ul nu există deja, creează-l */
dacă (Mapping == (HV*)NULL)
Mapare = newHV();
/* Salvați fh -> mapare callback */
hv_store(Mapping, (char*)&fh, sizeof(fh), newSVsv(callback), 0);
/* Înregistrați-vă la Biblioteca C */
asynch_read(fh, asynch_read_if);
și „asynch_read_if” ar putea arăta astfel
gol static
asynch_read_if(fh, buffer)
int fh;
char * buffer;
{
dSP;
SV ** sv;
/* Obține apelul invers asociat cu fh */
sv = hv_fetch(Mapping, (char*)&fh , sizeof(fh), FALSE);
dacă (sv == (SV**)NULL)
croak("Eroare internă...\n");
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(fh)));
XPUSHs(sv_2mortal(newSVpv(buffer, 0)));
PUNE INAPOI;
/* Apelați sub Perl */
call_sv(*sv, G_DISCARD);
}
Pentru a fi complet, aici este „asynch_close”. Acesta arată cum să eliminați intrarea din
hash „Mapping”.
anula
asynch_close(fh)
int fh
COD:
/* Eliminați intrarea din hash */
(void) hv_delete(Mapping, (char*)&fh, sizeof(fh), G_DISCARD);
/* Acum apelați adevăratul asynch_close */
asynch_close(fh);
Deci interfața Perl ar arăta așa
sub apel invers1
{
my($handle, $buffer) = @_;
}
# Înregistrați apelul Perl
asynch_read($fh, \&callback1);
asynch_close($fh);
Maparea dintre callback-ul C și Perl este stocată în hash global „Mapping” this
timp. Utilizarea unui hash are avantajul distinct că permite un număr nelimitat de
apelurile inverse trebuie înregistrate.
Ce se întâmplă dacă interfața oferită de apel invers C nu conține un parametru care să permită
mânerul fișierului la maparea subrutinei Perl? Spuneți în pachetul de i/o asincron, the
Funcția de apel invers i se trece doar parametrul „buffer” ca acesta
anula
ProcessRead(buffer)
char * buffer;
{
...
}
Fără handle-ul fișierului, nu există o modalitate simplă de a mapa de la apel invers C la
Subrutină Perl.
În acest caz, o posibilă modalitate de a evita această problemă este predefinirea unei serii de funcții C pentru
acționează ca interfață cu Perl, astfel
#define MAX_CB 3
#define NULL_HANDLE -1
typedef void (*FnMap)();
struct MapStruct {
Funcția FnMap;
SV * PerlSub;
int Mâner;
};
static void fn1();
static void fn2();
static void fn3();
struct static MapStruct Map [MAX_CB] =
{
{ fn1, NULL, NULL_HANDLE },
{ fn2, NULL, NULL_HANDLE },
{ fn3, NULL, NULL_HANDLE }
};
gol static
Pcb(index, buffer)
index int;
char * buffer;
{
dSP;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSVpv(buffer, 0)));
PUNE INAPOI;
/* Apelați sub Perl */
call_sv(Hartă[index].PerlSub, G_DISCARD);
}
gol static
fn1 (tampon)
char * buffer;
{
Pcb(0, buffer);
}
gol static
fn2 (tampon)
char * buffer;
{
Pcb(1, buffer);
}
gol static
fn3 (tampon)
char * buffer;
{
Pcb(2, buffer);
}
anula
array_asynch_read(fh, callback)
int fh
SV * apel invers
COD:
index int;
int null_index = MAX_CB;
/* Găsiți același mâner sau o intrare goală */
pentru (index = 0; index < MAX_CB; ++index)
{
dacă (Hartă[index].Handle == fh)
rupe;
dacă (Hartă[index].Handle == NULL_HANDLE)
null_index = index;
}
dacă (index == MAX_CB && null_index == MAX_CB)
croak ("Prea multe funcții de apel invers înregistrate\n");
dacă (index == MAX_CB)
index = null_index;
/* Salvează manerul fișierului */
Harta[index].Handle = fh;
/* Amintiți-vă de sub Perl */
dacă (Hartă[index].PerlSub == (SV*)NULL)
Hartă[index].PerlSub = newSVsv(callback);
altfel
SvSetSV(Hartă[index].PerlSub, apel invers);
asynch_read(fh, Map[index].Function);
anula
array_asynch_close(fh)
int fh
COD:
index int;
/* Găsiți identificatorul fișierului */
pentru (index = 0; index < MAX_CB; index ++)
dacă (Hartă[index].Handle == fh)
rupe;
dacă (index == MAX_CB)
croak ("nu s-a putut închide fh %d\n", fh);
Hartă[index].Handle = NULL_HANDLE;
SvREFCNT_dec(Harta[index].PerlSub);
Hartă[index].PerlSub = (SV*)NULL;
asynch_close(fh);
În acest caz, funcțiile „fn1”, „fn2” și „fn3” sunt folosite pentru a reține Perl
subrutină care urmează să fie apelată. Fiecare dintre funcții deține un index separat, care este
folosit în funcția „Pcb” pentru a accesa matricea „Hartă” și pentru a apela efectiv Perl
subrutină.
Există câteva dezavantaje evidente cu această tehnică.
În primul rând, codul este mult mai complex decât în exemplul anterior.
În al doilea rând, există o limită cablată (în acest caz 3) pentru numărul de apeluri inverse care pot
exista simultan. Singura modalitate de a crește limita este prin modificarea codului de adăugat
mai multe funcții și apoi recompilare. Cu toate acestea, atâta timp cât este numărul de funcții
ales cu puțină grijă, este încă o soluție viabilă și în unele cazuri este singura
disponibile.
Pentru a rezuma, iată o serie de metode posibile pe care trebuie să le luați în considerare pentru stocarea
mapare între C și apel invers Perl
1. Ignorați problema - Permiteți doar 1 apel invers
Pentru multe situații, cum ar fi interfața cu un handler de erori, acesta poate fi a
soluție perfect adecvată.
2. Creați o secvență de apeluri inverse - limită cablată
Dacă este imposibil să spunem din parametrii trecuți înapoi de la callback C ce
contextul este, atunci poate fi necesar să creați o secvență de interfață de apel invers C
funcții și stochează pointeri către fiecare într-o matrice.
3. Utilizați un parametru pentru a mapa cu apelul invers Perl
Un hash este un mecanism ideal pentru a stoca maparea dintre C și Perl.
Alterna Stivui Manipulare
Deși am folosit doar macrocomenzile „POP*” pentru a accesa valorile returnate de la Perl
subrutine, este, de asemenea, posibil să ocoliți aceste macrocomenzi și să citiți stiva folosind „ST”
macro (Vezi perlxs pentru o descriere completă a macrocomenzii „ST”).
De cele mai multe ori macrocomenzile „POP*” ar trebui să fie adecvate; principala problemă cu ele este că
vă obligă să procesați valorile returnate în succesiune. Acesta poate să nu fie cel mai mult
mod adecvat de procesare a valorilor în unele cazuri. Ceea ce ne dorim este să putem accesa
stivuiți într-o ordine aleatorie. Macro-ul „ST”, așa cum este utilizat la codificarea unui XSUB este ideal pentru aceasta
scop.
Codul de mai jos este exemplul dat în secțiune Revenind a Listă of Valori recodat la
utilizați „ST” în loc de „POP*”.
gol static
call_AddSubtract2(a, b)
int a;
int b;
{
dSP;
I32 topor;
număr de int;
INTRODUCE;
SAVETMPS;
PUSHMARK(SP);
XPUSHs(sv_2mortal(newSViv(a)));
XPUSHs(sv_2mortal(newSViv(b)));
PUNE INAPOI;
count = call_pv("AddSubtract", G_ARRAY);
SPANIA;
SP -= număr;
ax = (SP - PL_stack_base) + 1;
dacă (număr != 2)
croak(„Necazuri mari\n”);
printf ("%d + %d = %d\n", a, b, SvIV(ST(0)));
printf ("%d - %d = %d\n", a, b, SvIV(ST(1)));
PUNE INAPOI;
FREETMPS;
PĂRĂSI;
}
notițe
1. Observați că a fost necesară definirea variabilei „ax”. Acest lucru se datorează faptului că „ST”
macro se așteaptă să existe. Dacă am fi într-un XSUB nu ar fi necesar să definim
„topor” așa cum este deja definit pentru noi.
2. Codul
SPANIA;
SP -= număr;
ax = (SP - PL_stack_base) + 1;
setează stiva astfel încât să putem folosi macro-ul „ST”.
3. Spre deosebire de codarea originală a acestui exemplu, valorile returnate nu sunt accesate în
ordine inversă. Asa de ST(0) se referă la prima valoare returnată de subrutina Perl
iar „ST(count-1)” se referă la ultimul.
Crearea și apel an Anonim Subrutină in C
După cum am arătat deja, „call_sv” poate fi folosit pentru a invoca o subrutină anonimă. In orice caz,
exemplul nostru a arătat un script Perl care invocă un XSUB pentru a efectua această operație. Să vedem
cum se poate face în codul nostru C:
...
SV *cvrv = eval_pv("sub { print 'Nu mă veți găsi aglomerat niciun spațiu de nume!' }", TRUE);
...
call_sv(cvrv, G_VOID|G_NOARGS);
„eval_pv” este folosit pentru a compila subrutina anonimă, care va fi valoarea returnată ca
bine (citește mai multe despre „eval_pv” în „eval_pv” în perlapi). Odată ce această referință de cod este introdusă
de mână, poate fi amestecat cu toate exemplele anterioare pe care le-am arătat.
UȘOR Apeluri inverse
Uneori trebuie să invocați aceeași subrutină în mod repetat. Acest lucru se întâmplă de obicei cu a
funcție care acționează pe o listă de valori, cum ar fi încorporarea lui Perl fel(). Puteți trece o
funcția de comparație la fel(), care va fi apoi invocat pentru fiecare pereche de valori care
trebuie comparat. The primul() și reduce() funcțiile din List::Util urmează un similar
model.
În acest caz, este posibil să accelerați rutina (deseori destul de substanțial) prin folosire
API-ul ușor de apel invers. Ideea este că contextul apelării trebuie doar să fie
creat și distrus o dată, iar sub-ul poate fi numit arbitrar de multe ori între ele.
Este obișnuit să se transmită parametrii folosind variabile globale (de obicei $_ pentru un parametru sau
$a și $b pentru doi parametri) mai degrabă decât prin @_. (Este posibil să utilizați mecanismul @_
dacă știi ce faci, deși nu există încă nicio API acceptată pentru asta. Este de asemenea
în mod inerent mai lent.)
Modelul apelurilor macro este astfel:
dMULTICALL; /* Declara variabile locale */
I32 gimme = G_SCALAR; /* contextul apelului: G_SCALAR,
* G_ARRAY sau G_VOID */
PUSH_MULTICALL(cv); /* Configurați contextul pentru apelarea cv,
și setați valorile locale în mod corespunzător */
/* buclă */ {
/* setați valoarea (valorile) variabilelor dvs. de parametri */
MULTICAL; /* Efectuați apelul propriu-zis */
} /* sfârșitul buclei */
POP_MULTICALL; /* Dărâmă contextul apelării */
Pentru câteva exemple concrete, vezi implementarea programului primul() și reduce() funcții
din Listă::Util 1.18. Acolo veți găsi, de asemenea, un fișier antet care emulează API-ul multicall
pe versiunile mai vechi de perl.
Utilizați perlcall online folosind serviciile onworks.net