POD2-IT
view release on metacpan or search on metacpan
IT/perlxstut.pod view on Meta::CPAN
Tutto ciE<ograve> che precede questa riga E<egrave> codice C puro, che descrive quali
intestazioni includere, e definisce alcune funzioni di convenienza.
In questa sezione non viene effettuata alcuna traduzione: a parte
l'eliminazione della documentazione POD immersa (vedere L<perlpod>), il
contenuto viene messo cosE<igrave> com'E<egrave> nel file C generato.
Tutto ciE<ograve> che segue questa riga costituisce la descrizione delle funzioni
XSUB. Queste descrizioni sono tradotte da B<xsubpp> in codice C che
implementa le funzioni utilizzando le convenzioni di chiamata di Perl,
e che dunque rende tale funzioni visibili all'interprete Perl.
Prestate particolare attenzione alla funzione C<constant>. Questo nome
appare due volte nel file C<.xs> generato: una nella prima parte, come
funzione C statica, l'altra nella seconda parte, quando viene definita
un'interfaccia XSUB alla funzione C statica suddetta.
Questo E<egrave> piuttosto tipico in un file C<.xs>: di solito, il file C<.xs>
fornisce un'interfaccia ad una funzione C esistente. Questa funzione C
E<egrave> definita da qualche parte (una libreria esterna, o nella prima parte
del file C<.xs>), e un'interfaccia Perl per questa funzione (ossia, la
"colla con il Perl") E<egrave> descritta nella seconda parte del file C<.xs>.
La situazione in L<ESEMPIO 1>, L<ESEMPIO 2> e L<ESEMPIO 3>, ove tutto
il lavoro viene fatto dentro il "collante", E<egrave> piE<ugrave> un'eccezione che la
regola.
=head2 Tirare fuori la "ciccia" dalle XSUB
Nell'L<ESEMPIO 4> la seconda parte del file C<.xs> contiene la
seguente descrizione di una XSUB:
double
foo(a,b,c)
int a
long b
const char * c
OUTPUT:
RETVAL
Osservate che, contrariamente a quanto riportato in L<ESEMPIO 1>,
L<ESEMPIO 2> e L<ESEMPIO 3>, tale descrizione non contiene il I<codice>
vero e proprio di cosa viene fatto quando viene chiamata la funzione
Perl C<foo()>. Per capire cosa sta succedendo, si puE<ograve> aggiungere una
sezione CODE a questa XSUB:
double
foo(a,b,c)
int a
long b
const char * c
CODE:
RETVAL = foo(a,b,c);
OUTPUT:
RETVAL
In ogni caso, queste due XSUB forniscono un codice C generato praticamente
uguale: il compilatore B<xsubpp> E<egrave> abbastanza intelligente da intuire
la sezione C<CODE:> dalle prime due righe della descrizione XSUB. Che
dire della sezione C<OUTPUT:>? E<Egrave> assolutamente la stessa! Anche la
sezione C<OUTPUT:> puE<ograve> essere rimossa, I<sempre che le sezioni C<CODE:>
o C<PPCODE:> > non siano specificate: B<xsubpp> puE<ograve> quindi vedere che ha
bisogno di generare una sezione di chiamata a funzione, e genererE<agrave> anche
la sezione C<OUTPUT>. Per quanto detto, un'abbreviazione della XSUB
diventa:
double
foo(a,b,c)
int a
long b
const char * c
Possiamo fare la stessa cosa con la XSUB
int
is_even(input)
int input
CODE:
RETVAL = (input % 2 == 0);
OUTPUT:
RETVAL
dell'L<ESEMPIO 2>? Per farlo, avremmo bisogno di definire una funzione
C C<int is_even(int input)>. Come abbiamo visto il L<Anatomia di un file C<.xs> >, un possibile posto per questa definizione si trova nella prima parte
del file C<.xs>. A tutti gli effetti, una funzione
int
is_even(int arg)
{
return (arg % 2 == 0);
}
E<egrave> probabilmente troppo in questo caso. Qualcosa di piE<ugrave> semplice come una
C<#define> servirE<agrave> egregiamente allo scopo:
#define is_even(arg) ((arg) % 2 == 0)
Una volta inserito nella prima parte del file C<.xs>, la "colla Perl"
diventa semplice:
int
is_even(input)
int input
Questa tecnica di separazione della parte "collante" da quella che
"lavora" presenta anche degli svantaggi: se volete cambiare un'interfaccia
Perl, dovete farlo in due punti del vostro codice. In ogni caso, vi
consente di rimuovere parecchio rumore, e rende la parte "che lavora"
indipendente dalle idiosincrasie della convenzione di chiamata di Perl.
(Infatti, non c'E<egrave> niente di specifico di Perl nella descrizione data; una
versione di B<xsubpp> differente avrebbe potuto anche tradurlo in codice
collante TCL o Python).
=head2 Ancora sugli argomenti XSUB
Con il completamento dell'L<ESEMPIO 4>, abbiamo ora un modo semplice per
simulare alcune librerie "reali" la cui interfaccia potrebbe non essere
la piE<ugrave> pulita al mondo. Continueremo ora con una discussione degli argomenti
passati al compilatore B<xsubpp>.
Quando specificate gli argomenti per le routine nel file C<.xs>, in realtE<agrave>
state passando tre informazioni per ciascuno degli argomenti. La prima
IT/perlxstut.pod view on Meta::CPAN
=head2 Documentare la vostra Estensione
Non avete assolutamente alcuna scusa per evitare di documentare la vostra
estensione. La documentazione va posta nel file C<.pm>. Questo file
verrE<agrave> immesso in C<pod2man>, e la documentazione immersa verrE<agrave> convertita
nel formato delle I<manpage> [pagine del manuale stile Unix, N.d.T.] e
posta nella directory blib. VerrE<agrave> infine copiata nella directory delle
pagine di manuale di Perl quando l'estensione verrE<agrave> installata.
Potete alternare la documentazione ed il codice Perl all'interno del file
C<.pm>. In effetti, se volete utilizzare il metodo dell'auto-caricamento,
dovete fare proprio cosE<igrave>, come spiegano i commenti all'interno del file
C<.pm>.
Consultate L<perlpod> per maggiori informazioni sul formato pod.
=head2 Installare la vostra Estensione
Una volta che la vostra estensione E<egrave> completa e passa tutti i test,
l'installazione E<egrave> piuttosto semplice: non dovete far altro che lanciare
C<make install>. Avrete bisogno di avere i permessi in scrittura nella
directory dove E<egrave> installato Perl, o dovrete chiedere all'amministratore
di sistema di fare l'installazione per voi.
In alternativa, potete specificare la directory esatta dove porre i file
dell'estensione chiamando C<make install PREFIX=/directory/di/destinazione>
(potreste aver bisogno di insirire l'aggiunta fra C<make> e C<install> se
avete una versione di C<make> un po' bizzarra). Questo risulta
particolarmente utile se state costruendo un'estensione che verrE<agrave>
distribuita in molti sistemi. Potete allora semplicemente archiviare i
file nella directory di destinazione, e distribuire questo archivio
nei sistemi di destinazione.
=head2 ESEMPIO 5
In questo esempio avremo di nuovo a che fare con lo stack degli argomenti.
Gli esempi precedenti hanno tutti restituito un solo, unico valore; qui
creeremo un'estensione che restituisce un array.
Questa estensione E<egrave> molto polarizzata verso Unix (utilizza
C<struct statfs> e la chiamata di sistema C<statfs>). Se non vi trovate
su un sistema Unix, potete sostituire a C<statfs> una qualsiasi altra
funzione che restituisce piE<ugrave> valori, potete scrivere direttamente i valori
che devono essere restituiti (sebbene questo risulterE<agrave> un po' piE<ugrave>
difficile da controllare in caso di condizioni di errore) o potete
semplicemente saltare questo esempio. Se cambiate la XSUB, assicuratevi
di cambiare anche i test in modo da allineare i cambiamenti.
Tornate nela directory MiaProva e aggiungete il seguente codice alla fine
di C<MiaProva.xs>:
void
statfs(path)
char * path
INIT:
int i;
struct statfs buf;
PPCODE:
i = statfs(path, &buf);
if (i == 0) {
XPUSHs(sv_2mortal(newSVnv(buf.f_bavail)));
XPUSHs(sv_2mortal(newSVnv(buf.f_bfree)));
XPUSHs(sv_2mortal(newSVnv(buf.f_blocks)));
XPUSHs(sv_2mortal(newSVnv(buf.f_bsize)));
XPUSHs(sv_2mortal(newSVnv(buf.f_ffree)));
XPUSHs(sv_2mortal(newSVnv(buf.f_files)));
XPUSHs(sv_2mortal(newSVnv(buf.f_type)));
XPUSHs(sv_2mortal(newSVnv(buf.f_fsid[0])));
XPUSHs(sv_2mortal(newSVnv(buf.f_fsid[1])));
} else {
XPUSHs(sv_2mortal(newSVnv(errno)));
}
Avrete anche bisogno di aggiungere quanto segue all'inizio del file
C<.xs>, subito dopo l'inclusione di C<XSUB.h>:
#include <sys/vfs.h>
Aggiungete anche il seguente frammento di codice a C<test.pl>,
ricordandovi di incrementare la stringa "1..9" in "1..11"
nel blocco C<BEGIN>:
@a = &MiaProva::statfs("/maddeche");
print ((scalar(@a) == 1 && $a[0] == 2) ? "ok 10\n" : "not ok 10\n");
@a = &MiaProva::statfs("/");
print scalar(@a) == 9 ? "ok 11\n" : "not ok 11\n";
=head2 Cose Nuove in questo Esempio
Questo esempio ha aggiunto un po' di concetti nuovi, li analizzeremo
uno alla volta.
=over 4
=item *
La direttiva C<INIT:> contiene codice che verrE<agrave> inserito immediatamente
dopo che lo stack degli argomenti viene decodificato. Il linguaggio C
non consente dichiarazioni di variaible in posizioni arbitrarie
all'interno di una funzione, per cui questa E<egrave> di norma la soluzione migliore
per dichiarare le variabili locali di cui la XSUB ha bisogno. (In
alternativa, E<egrave> possibile mettere l'intera sezione C<PPCODE:> in
parentesi graffe, ed aggiungere queste dichiarazioni all'inizio).
=item *
Questa routine restituisce anche un numero di argomenti differenti
dipendentemente dal fatto che la chiamata a C<statfs> abbia successo
o meno. Se ci sono errori, viene restituito il numero dell'errore
come unico elemento di un array. Se la chiamata ha successo, viene
restituito un array di 9 elementi. PoichE<eacute> questa funzione riceve un solo
argomento, abbiamo bisogno di fare spazio sullo stack per tenere i
9 valori che potrebbero essere restituiti.
Otteniamo tutto ciE<ograve> utilizzando la direttiva C<PPCODE:>, piuttosto che
la direttiva C<CODE:>. Questo indica a B<xsubpp> che utilizzeremo i
valori di ritorno che saranno messi sullo stack degli argomenti da noi
stessi.
=item *
Quando vogliamo inserire i valori da restituire al chiamante sullo stack,
utilizziamo qualla serie di marco che cominciano con C<XPUSH>. Ce ne sono
cinque versioni differenti a seconda dei tipi delle variabili: interi,
interi senza segno, I<double>, stringhe e scalari Perl. Nel nostro
esempio abbiamo inserito uno scalare Perl sullo stack. (Infatti questa
E<egrave> l'unica macro che puE<ograve> essere utilizzata per restituire valori
multipli).
Le macro C<XPUSH*> estenderanno lo stack di ritorno automaticamente, in
modo da prevenire possibili riempimenti. Dovete inserire i valori
nello stack nello stesso ordine con cui li volete vedere nel programma
chiamante.
=item *
I valori inseriti nello stack di ritorno della XSUB sono in realtE<agrave>
degli SV I<mortali>. Sono resi tali in modo che una volta che i valori
vengono copiati dal programma chiamate, le variabili SV che li
contengono possono essere deallocate. Se non fossero I<mortali>, infatti,
continuerebbero ad esistere dopo il ritorno dalla XSUB, senza perE<ograve>
essere accessibili; questo risulterebbe dunque in uno spreco di memoria.
=item *
Se fossimo interessati alle prestazioni, ma non nella compattezza del
codice, nel ramo relativo ad una chiamata andata a buon fine non
utilizzeremmo le macro C<XPUSH*>, ma quelle C<PUSH>, pre-estendendo
lo stack una volta per tutte prima di inserire i valori:
EXTEND(SP, 9);
Per contro, si ha bisogno di calcolare in anticipo il numero di elementi
da restituire (sebbene estendere lo stack oltre quanto necessario
non abbia tipicamente altre conseguenze se non un maggior consumo di memoria).
In maniera analoga, nel ramo in cui la chiamata a C<statfs> fallisce
potremmo utilizzare C<PUSH> I<senza> estendere lo stack: il riferimento
alla funzione Perl arriva alla XSUB sullo stack, per cui c'E<egrave> I<sempre>
spazio sufficiente per un singolo valore da restituire.
=back
=head2 ESEMPIO 6
In questo esempio riceveremno un riferimento ad un array come parametro
di ingresso, e restituiremo un riferimento ad un array di hash. Potremo
in questo modo dimostrare come sia possibile manipolare strutture dati
Perl complesse all'interno di una XSUB.
L'estensione di questo esempio E<egrave> artificiosa. E<Egrave> basata
sul codice dell'esempio precedente; chiama la funzione C<statfs> piE<ugrave>
volte, accettando in ingresso un riferimento ad un array di nomi di
file, e restituendo un riferimento ad un array di hash, ciascuna
contenente i dati per i filesystem.
IT/perlxstut.pod view on Meta::CPAN
INIT:
AV * results;
I32 numpaths = 0;
int i, n;
struct statfs buf;
if ((!SvROK(paths))
|| (SvTYPE(SvRV(paths)) != SVt_PVAV)
|| ((numpaths = av_len((AV *)SvRV(paths))) < 0))
{
XSRETURN_UNDEF;
}
results = (AV *)sv_2mortal((SV *)newAV());
CODE:
for (n = 0; n <= numpaths; n++) {
HV * rh;
STRLEN l;
char * fn = SvPV(*av_fetch((AV *)SvRV(paths), n, 0), l);
i = statfs(fn, &buf);
if (i != 0) {
av_push(results, newSVnv(errno));
continue;
}
rh = (HV *)sv_2mortal((SV *)newHV());
hv_store(rh, "f_bavail", 8, newSVnv(buf.f_bavail), 0);
hv_store(rh, "f_bfree", 7, newSVnv(buf.f_bfree), 0);
hv_store(rh, "f_blocks", 8, newSVnv(buf.f_blocks), 0);
hv_store(rh, "f_bsize", 7, newSVnv(buf.f_bsize), 0);
hv_store(rh, "f_ffree", 7, newSVnv(buf.f_ffree), 0);
hv_store(rh, "f_files", 7, newSVnv(buf.f_files), 0);
hv_store(rh, "f_type", 6, newSVnv(buf.f_type), 0);
av_push(results, newRV((SV *)rh));
}
RETVAL = newRV((SV *)results);
OUTPUT:
RETVAL
Aggiungete anche quanto segue allo script C<test.pl>, portando il numero
di test a "1..13" nel blocco C<BEGIN>:
$results = MiaProva::multi_statfs([ '/', '/maddeche' ]);
print ((ref $results->[0]) ? "ok 12\n" : "not ok 12\n");
print ((! ref $results->[1]) ? "ok 13\n" : "not ok 13\n");
=head2 Cose Nuove in questo Esempio
Ci sono un certo numero di concetti nuovi, descritti qui a seguire.
=over 4
=item *
Questa funzione non utilizza una C<typemap>. Al contrario, dichiariamo che
accetta un parametro C<SV*> (scalare), e che restituisce un valore C<SV*>;
ci prendiamo direttamente cura di questi scalari all'interno del codice.
PoichE<eacute> stiamo restituendo un solo valore, non abbiamo bisogno di una
direttiva C<PPCODE:> - al contrario, utilizziamo le direttive C<CODE:> e
C<OUTPUT:>.
=item *
Quando si ha a che fare con i riferimenti, E<egrave> importante trattarli con
cautela. Il blocco C<INIT:> prima di tutto si assicura che C<SvROK>
abbia valore vero, il che indica che C<paths> E<egrave> un riferimento valido.
Successivamente verifica che l'oggetto riferito da C<paths> sia un array,
utilizzando C<SvRV> per dereferenziarlo, e C<SvTYPE> per scoprire qual E<egrave>
il suo tipo. Come controllo addizionale verifica che l'array sia non
vuoto, utilizzando la funzione C<av_len> (la quale restituisce -1 quando
l'array E<egrave> vuoto). La macro C<XSRETURN_UNDEF> viene utilizzata per
abortire la XSUB e restituire il valore C<undef> laddove le condizioni
suddette non siano raggiunte.
=item *
Manipoliamo parecchi array in questa XSUB. Osservate che un array viene
rappresentato internamente attraverso un puntatore C<AV*>. Le funzioni e
le macro per manipolare gli array sono simili alle funzioni in Perl:
C<av_len> restituisce l'indice piE<ugrave> elevato in un C<AV*>, similmente
a C<$#array>; C<av_fetch> prende un singolo valore scalare da un array,
dato il suo indice; C<av_push> inserisce un valore scalare in fondo
all'array, estendendolo automaticamente se necessario.
Nello specifico, leggiamo i nomi dei percorsi uno alla volta dall'array
di ingresso, ed inseriamo i risultati in un array di uscita (risultati)
nello stesso ordine. Se C<statfs> fallisce, l'elemento inserito
nell'array di uscita E<egrave> il valore di C<errno> dopo tale fallimento. Se
al contrario C<statfs> va a buon fine, il valore inserito nell'array
di uscita E<egrave> un riferimento ad una hash contenente alcune delle
informazioni contenute nella struttura C<struct statfs>.
CosE<igrave> come per lo stack di uscita, sarebbe possibile (ottenedo un
piccolo vantaggio di prestazioni) pre-estendere l'array di uscita prima
di inserirvi i dati, poichE<eacute> sappiamo in anticipo quanti elementi
dobbiamo restituire:
av_extend(results, numpaths);
=item *
In questa funzione stiamo solo effettuando un'operazione su hash, ossia
immagazzinando un nuovo scalare relativo ad una data chiave utilizzando
C<hv_store>. Una hash E<egrave> rappresentata attraverso un puntatore C<HV*>.
Come gli array, le funzioni per manipolare le hash in una XSUB
rispecchiano le funzionalitE<agrave> disponibili direttamente in Perl.
Per i dettagli consultate L<perlguts> e L<perlapi>.
=item *
Per creare un riferimento, utilizziamo la funzione C<newRV>. Osservate
che potete trasformare un C<AV*> o un C<HV*> in un tipo C<SV*> in questo
come in molti altri casi. CiE<ograve> rende possibile prendere riferimenti ad
array, hash e scalari con la stessa funzione. Di contro, la funzione
C<SvRV> restituisce sempre un C<SV*>, che potrebbe aver bisogno di
essere trasformata nel tipo appropriato se deve rappresentare qualcosa
di differente da uno scalare (si puE<ograve> verificare con C<SvTYPE>).
=item *
( run in 1.243 second using v1.01-cache-2.11-cpan-5511b514fd6 )