2013-08-17 8 views
10

Non riesco a far passare arrayrefs in una funzione C utilizzando Inline C. Vorrei un aiuto, per favore.Perl Inline C: passaggio della funzione Arrayref a C

In primo luogo, solo per dimostrare che posso ottenere in linea C per funzionare, io passo un valore scalare a una funzione C:

#!/usr/bin/perl -I. 
# 
# try1.pl 
# 
use Inline C; 

my $c = 3.8; 
foo($c); 

__END__ 
__C__ 
void foo(double c) 
{ 
    printf("C = %f\n", c); 
} 

ed eseguirlo:

% ./try1.pl 
C = 3.800000 

Ora fare il stessa cosa, ma con un rifarray:

#!/usr/bin/perl -I. 
# 
# try2.pl 
# 
use Inline C; 

my @abc = (1.9, 2.3, 3.8); 
foo(\@abc); 

__END__ 
__C__ 
void foo(double *abc) 
{ 
    printf("C = %f\n", abc[2]); 
} 

eseguirlo:

012.
% ./try2.pl 
Undefined subroutine &main::foo called at ./try1.pl line 7. 

Qualche idea su cosa sto facendo male? Aiuto molto apprezzato!

+0

Sono nuovo in Perl, ma la riga 7 è la linea su cui si definisce la matrice, si sta definendo la matrice correttamente. Stai passando in modo corretto (con il /?) Ha bisogno di essere convertito in un array C tramite qualcosa in A.P.I (Non come funziona XD)? –

risposta

4

Tipo di dati errato.

use Inline 'C'; 

my @abc = (1.9, 2.3, 3.8); 
foo(\@abc); 

__END__ 
__C__ 
void foo(SV* abc) { 
    sv_dump(abc); 
} 
+0

Grazie per la risposta. E come potrei accedere al 3 ° elemento di SV * abc? – AndyFroncioni

4

Nel codice Inline::C:

void foo(SV *reference) { 
    AV *array; 

    array = (AV *)SvRV(reference); 

    ... 
} 

poi trattare con il valore di matrice come il tipo AV. Vedi lo Perl Inline::C Cookbook.

+0

Perché non solo "void foo (array AV *) {...'? – mob

11

Inline :: C è abbastanza intelligente per estrarre i valori da SV in base alla firma del tipo di funzione C. Ma se vuoi passare strutture complesse in Perl a funzioni C, devi usare lo Perl API per estrarre i valori. Quindi, ecco cosa è necessario sapere per questo problema:

Una matrice è un'istanza di un C struct chiamato AV. Un riferimento è implementato da struct chiamato RV. Tutti questi sono "sottotipi" (kinda) di una base struct denominata SV.

Quindi per far funzionare questa funzione dobbiamo fare alcune cose.

  1. Modificare il tipo di parametro su SV * (puntatore su SV).
  2. utilizzare l'API per verificare se questo particolare SV è un riferimento al contrario di qualche altro tipo di scalare
  3. Controllare il camper per assicurarsi che sta puntando a un array e non qualcos'altro.
  4. Dereference the RV per ottenere il SV a cui punta.
  5. Poiché sappiamo che SV è una matrice, lanciarla su AV e iniziare a lavorarci.
  6. Cerca il terzo elemento di tale array, che è un altro SV.
  7. Verificare che il SV abbiamo ottenuto dalla matrice è un valore numerico adatto per C printf
  8. Estrarre il numerico effettivo dal SV.
  9. Stampare il messaggio

Quindi mettere che tutti insieme, si ottiene qualcosa di simile:

use Inline C; 

my @abc = (1.9, 2.3, 3.8); 
foo(\@abc); 

__END__ 
__C__ 
void foo(SV *abc) 
{ 
    AV *array;  /* this will hold our actual array */ 
    SV **value; /* this will hold the value we extract, note that it is a double pointer */ 
    double num; /* the actual underlying number in the SV */ 

    if (!SvROK(abc)) croak("param is not a reference"); 
    if (SvTYPE(SvRV(abc)) != SVt_PVAV) croak("param is not an array reference"); 

    /* if we got this far, then we have an array ref */ 
    /* now dereference it to get the AV */ 
    array = (AV *)SvRV(abc); 

    /* look up the 3rd element, which is yet another SV */ 
    value = av_fetch(array, 2, 0); 

    if (value == NULL) croak("Failed array lookup"); 
    if (!SvNOK(*value)) croak("Array element is not a number"); 

    /* extract the actual number from the SV */ 
    num = SvNV(*value); 

    printf("C = %f\n", num); 
} 

fa Kinda apprezzare quanto lavoro Perl fa sotto il cofano. :)

Ora, non devi essere tanto esplicito quanto quell'esempio. Puoi liberarti di alcune variabili temporanee facendo cose in linea, ad es.

printf("C = %f\n", SvNV(*value)); 

eliminerebbe la necessità di dichiarare num. Ma ho voluto far capire quanto è necessario dereferenziazione e il tipo di controllo per attraversare una struttura Perl in C.

E come @mob sottolinea qui di seguito, in realtà non hanno a che fare tutto che lavorano (se è una buona idea di essere a conoscenza di come funziona.)

Inline :: C è abbastanza intelligente che se si dichiara la funzione come

void foo(AV *abc) { 
    ... 
} 

sarà scartare automaticamente la AV per voi e si può andare direttamente al passo av_fetch.

Se tutto questo sembra sconcertante a voi, mi raccomando dare un'occhiata a:

  1. Il Perlguts Illustrated PDF, quindi
  2. Il perlguts pagina di manuale, e quindi
  3. Il Inline::C Cookbook, consultando
  4. La manpage perlapi.
+2

OK per dichiarare la funzione 'void foo (AV * array) {...' e lasciare che Perl/Inline :: C gestisca i primi tre passi. – mob

+0

Buon punto @mob; aggiornato. – friedo

+0

Che risposta completa @friedo. Grazie anche a te! – AndyFroncioni