2012-02-24 24 views
6

C'è un libx.so che esportano 2 funzioni, e un struct,C struct in Python

typedef struct Tag { 
    int num; 
    char *name; 
}Tag; 

Tag *create(int n, char *n) 
{ 
    Tag *t = malloc(sizeof(Tag)); 
    t->num = n; 
    t->name = n; 
    return t; 
} 

void use(Tag *t) 
{ 
    printf("%d, %s\n", t->num, t->name); 
} 

voglio chiamare create in Python e quindi salvare il Tag *res restituito da create, poi chiamerò use e passaggio il Tag *res salvato prima di use, qui è esso (solo per dimostrare):

>>>libx = ctypes.CDLL("./libx.so") 
>>>res = libx.create(c_int(1), c_char_p("a")) 
>>>libx.use(res) 

il codice di cui sopra potrebbe essere sbagliato, proprio per dimostrare quello che voglio fare.

E il mio problema è che, come posso salvare il risultato restituito da create? Perché restituisce un puntatore a un struct definito dall'utente e non voglio costruire la controparte di struct Tag in Python, sarebbe c_void_p fare il trucco?

UPDATE

Da @ risposta di David, io ancora non capisco una cosa:

il puntatore (c_char_p("a")) è valido solo per la durata della chiamata a create. Appena creato restituisce quel puntatore non è più valido .

E assegno c_char_p("a") a t->name in create, quando la chiamata al create finiture, è t->name un puntatore penzoloni? Perché secondo le parole citate, quel puntatore non è più valido dopo create. Perché c_char_p("a") non è più valido?

risposta

4

Il codice C che si presenta semplicemente non funziona. È necessario essere molto più precisi su quale parte si assegna ed è responsabile della memoria heap.

Nell'esempio corrente si passa c_char_p("a") al codice C. Tuttavia, il puntatore a quella memoria di tipo ctypes è valido solo per la durata della chiamata a create. Appena create ritorna, quel puntatore non è più valido. Ma hai preso una copia del puntatore all'interno di create. Pertanto la successiva chiamata a use potrebbe fallire.

È necessario prendere una copia del contenuto di quella stringa e memorizzarla nella struttura. Se lo fai, puoi usare libx.create.restype = c_void_p in sicurezza.

Ma se si desidera che la memoria allocata sia deallocata, è necessario fornire una funzione destroy corrispondente alla funzione create. Con queste modifiche il codice C sarebbe simile a questa:

Tag *create(int n, char *s) 
{ 
    Tag *t = malloc(sizeof(Tag)); 
    t->num = n; 
    t->name = strdup(s); 
    return t; 
} 

void destroy(Tag *t) 
{ 
    free(t->name); 
    free(t); 
} 

Il codice Python sarebbe simile a questa:

libx = ctypes.CDLL("./libx.so") 
libx.create.restype = c_void_p 
res = libx.create(c_int(1), c_char_p("a")) 
libx.use(res) 
libx.destroy(res) 
+0

Sono appena andato puzzolente nei miei pantaloni –

+0

Bene, signore, ci sono 3 cose che non capisco. 1, 'il puntatore alla memoria di quel tipo è valido solo per la durata della chiamata da creare. Perché è così? Presumo che sia perché dopo la chiamata a 'create',' c_char_p ("a") 'il conteggio di rif. Va a zero, quindi' "a" 'è garbage-collected, giusto? 2, nel tuo codice, 'res' è un oggetto' c_void_p', ma 'libx.use' prende un' Tag * 'arg, posso semplicemente passare' res' direttamente a 'libx.use' senza cast? 3, perché dovrei esplicitamente 'destroy (res)'? – Alcott

+0

Ho appena eseguito un test, sembra che il mio codice possa funzionare correttamente, signore. – Alcott

0

Python fa il conteggio dei riferimenti. Dovrai utilizzare Py_INCREF() e gli amici per gli oggetti che vengono restituiti da librerie "esterne".

AGGIORNAMENTO: Non so. Sto caricando da Python, forse il metodo proposto da @David Hefferman lo fa automagicamente.

UPDATE2: cancellami!

+1

La domanda riguarda i tipi che è un'interfaccia di funzione straniera, simile a pinvoke se lo sai. 'Py_INCREF' semplicemente non è pertinente. –

+0

Ok. Cancellerò – wildplasser