2013-03-11 12 views
6

Sto provando a scrivere una funzione C tramite Inline :: C che creerà e restituirà un riferimento di matrice a Perl ... di seguito è riportato il mio script e l'output. Sto allocando la matrice e la popola correttamente? In particolare, creo un array temporaneo di SV* e li passo a av_make e restituisco un riferimento creato con newRV_noinc. I conteggi di riferimento sembrano buoni quando guardo il ref dell'array restituito con Devel :: Peek :: Dump, che sembra identico alla stessa struttura dati creata in perl direttamente.Come dovrei creare e restituire un arrayref in Inline :: C?

Non ho ancora capito cosa sia la mortalizzazione/sv_2mortal o se ne ho bisogno qui. Apparentemente, Inline :: C chiama automaticamente sv_2mortal sulle funzioni che restituiscono SV*, che possono essere rilevanti o meno.

#!/usr/bin/env perl 
use strict; 
use warnings FATAL => "all"; 
use 5.010_000; 
use Data::Dumper; 
use autodie; 

use Inline ('C'); 
use Devel::Peek; 

my $from_perl = [0 .. 9]; 
my $from_c = inline_array_maker(10); #same as above but in C 

say Dumper $from_perl; 
Dump($from_perl); 

say Dumper $from_c; 
Dump($from_c); 


__END__ 
__C__ 
SV* (int len){ 
    int i; 
    SV ** tmp_buffer; 
    AV * arr; 

    tmp_buffer = malloc(sizeof(SV*) * len); 

    printf("allocating tmp_buffer of size %d\n", len); 
    for (i = 0; i < len; i++) { 
     tmp_buffer[i] = newSViv(i); 
    } 

    // is av_make the most efficient way of doing this? 
    printf("av_make\n"); 
    arr = av_make(len, tmp_buffer); 

    printf("freeing tmp_buffer\n"); 
    for (i = 0; i < len; i++) { 
     sv_free(tmp_buffer[i]); 
    } 
    free(tmp_buffer); 

    return newRV_noinc(arr); 
} 

Ottengo il seguente output.

allocating tmp_buffer of size 10 
av_make 
freeing tmp_buffer 
$VAR1 = [ 
     0, 
     1, 
     2, 
     3, 
     4, 
     5, 
     6, 
     7, 
     8, 
     9 
     ]; 

SV = IV(0x20c7520) at 0x20c7530 
REFCNT = 1 
FLAGS = (PADMY,ROK) 
RV = 0x21c0fa8 
SV = PVAV(0x25c7ec8) at 0x21c0fa8 
    REFCNT = 1 
    FLAGS =() 
    ARRAY = 0x25a0e80 
    FILL = 9 
    MAX = 9 
    ARYLEN = 0x0 
    FLAGS = (REAL) 
    Elt No. 0 
    SV = IV(0x20b2dd8) at 0x20b2de8 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 0 
    Elt No. 1 
    SV = IV(0x20b2fb8) at 0x20b2fc8 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 1 
    Elt No. 2 
    SV = IV(0x20c69f8) at 0x20c6a08 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 2 
    Elt No. 3 
    SV = IV(0x20c6a10) at 0x20c6a20 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 3 
$VAR1 = [ 
     0, 
     1, 
     2, 
     3, 
     4, 
     5, 
     6, 
     7, 
     8, 
     9 
     ]; 

SV = IV(0x20d25c8) at 0x20d25d8 
REFCNT = 1 
FLAGS = (PADMY,ROK) 
RV = 0x25ac6b8 
SV = PVAV(0x25c7ea0) at 0x25ac6b8 
    REFCNT = 1 
    FLAGS =() 
    ARRAY = 0x25b9140 
    FILL = 9 
    MAX = 9 
    ARYLEN = 0x0 
    FLAGS = (REAL) 
    Elt No. 0 
    SV = IV(0x25aca80) at 0x25aca90 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 0 
    Elt No. 1 
    SV = IV(0x25ac750) at 0x25ac760 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 1 
    Elt No. 2 
    SV = IV(0x25ac5e8) at 0x25ac5f8 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 2 
    Elt No. 3 
    SV = IV(0x25ac930) at 0x25ac940 
    REFCNT = 1 
    FLAGS = (IOK,pIOK) 
    IV = 3 

risposta

7

Hai letto perldoc perlguts? Dovresti prenderti la maggior parte del modo lì. Inline::C può gestire anche un valore di ritorno AV*. Potrei fare qualcosa di simile:

#!/usr/bin/env perl 

use strict; 
use warnings; 

use Inline C => <<'END'; 
    AV* make_arrayref (int size) { 
    int i; 
    AV* ret = newAV(); 
    sv_2mortal((SV*)ret); 
    for(i=0; i<size; i++) { 
     av_push(ret, newSViv(i)); 
    } 
    return ret; 
    } 
END 

my $arrayref = make_arrayref(10); 
print "$_\n" for @$arrayref; 

Here is some code Ho scritto che potrebbe aiutare troppo.

+0

non sapevo di 'MUTABLE_SV', perché è preferibile? Dovrebbe essere aggiornato [this] (http://perldoc.perl.org/perlguts.html#Reference-Counts-and-Mortality) per non suggerire typecasting? –

+0

Si assicura che non si scriva 'const'. Presenterò una patch. – ikegami

+0

Si potrebbe voler applicare anche la perlapi, non è nemmeno documentata lì e senza di essa sarei riluttante ad usarla. In effetti, la vedo solo documentata nelle perldeltas, che non utilizzerei come riferimento API per non perdere la nota "rimossa" in un delta successivo. –

5

Non ho ancora capito cos'è mortalizzazione/sv_2mortal o se ne ho bisogno qui. Apparentemente, Inline :: C chiama automaticamente sv_2mortal sulle funzioni che restituiscono SV *, che può o non può essere rilevante.

Quando hai assegnato il camper, ne sei diventato il proprietario. Se rinunci alla tua presa su di esso, devi ridurre il suo conto. Ma questo lo libererebbe sul posto poiché nessun altro ha un riferimento ad esso. Mortalizzare è un decremento ritardato del conto. Dà al chiamante la possibilità di afferrarlo (e incrementare il suo conto) prima.

Quindi sì, il camper deve essere mortalizzato, e sì, la typemap mortalizzerà un SV * restituito per te.

Il codice è privo di errori, ma come ha dimostrato Joel Berger, non è necessario creare un array C. Anche il suo codice va bene.

Ha anche dimostrato che se il tipo di ritorno è AV*, la typemap creerà un riferimento (mortale) all'array, ma non mortalizzerà l'array stesso. Le tue due opzioni di ritorno primaria sono quindi

// Return type = SV* 
return newRV_noinc(arr); 

e

// Return type = AV* 
return MUTABLE_AV(sv_2mortal(MUTABLE_SV(newRV_noinc(arr))); 
+0

E grazie per aver espresso i miei pensieri meglio di quello che potevo avere sul perché stavo facendo cosa.Questa è una di quelle cose che riesco a malapena a mantenere per conto mio, per non parlare di spiegare. Saluti! –

+0

@Joel Berger, il mio obiettivo principale era quello di spiegare la mortalizzazione da quando ha chiesto a questo proposito. Le sovrapposizioni con il tuo nodo sono avvenute involontariamente. – ikegami

Problemi correlati