2013-07-17 16 views
5

Sto provando a creare un wrapper Python per un vecchio programma C che prende gli input come puntatori. A questo punto, posso avviare il programma ma non riesco a capire come recuperare i valori ai puntatori designati.Accesso ai dati all'indirizzo di memoria con tipi diversi

Questo è uno script C semplificata:

#include <stdlib.h> 

void cprogram(double *wts, double *res, int *kks, int n, double *ex) { 
    int m; 
    m=n+1; 
    res[0]=1.0; 
    kks[0]=1.0;} 

E questo è il mio codice Python semplificato:

from ctypes import * 
import sys 

libc = CDLL("src/program.so") 

class CONTEXT(Structure): 
    _fields_ = [ 
       ("wts", POINTER(c_double)), //tried just to see if it would work 
       ("res", c_double), 
       ("kks", c_int), 
       ("n", c_int), 
       ("ex", c_double)] 

def runner(): 
    kk = (1,2,3) 
    n = 3 
    mm = n + 1 
    wts = (c_double * n)(1, 1, 1) 
    res = (c_double * mm)(0) 
    kks = (c_int * len(kk))(*kk) 
    n = c_int(n) 
    ex = c_double(0) 

    libc.cprogram.restype = POINTER(CONTEXT) 

    tmp = libc.cprogram(wts, res, kks, n, ex) 

runner() 

ho cercato comandi come print tmp[1].wts[1] e print tmp[2] ma questo stampa solo l'indirizzo di memoria e non il valore (o valori incredibilmente piccoli errati come 2.15880221124e-314). Mi piacerebbe essere in grado di restituire un elenco dei valori di wts.

risposta

3

La funzione C restituisce void, non un CONTEXT *. Quindi, il tuo codice sta semplicemente trasmettendo una memoria non inizializzata casuale a uno CONTEXT * e poi provando a derefarlo.

Per di più, anche se fatto ritorno un oggetto CONTEXT per riferimento, tmp[1] avrebbe cercato di Deref memoria dopo quell'oggetto, quindi sarebbe ancora essere spazzatura.

Quando si tenta di interpretare la memoria casuale come un doppio, si sta andando ad ottenere segfaults o valori come 2.15880221124e-314 se sei fortunato, se siete sfortunati, si otterrà valori corretti di aspetto, ma ancora casuali come 0.0.


Nel frattempo, dal momento che la funzione C modifica i suoi argomenti in posizione, non è necessario fare nulla di fantasia qui. Basta usare le variabili avete passato

Quindi:.

def runner(): 
    kk = (1,2,3) 
    n = 3 
    mm = n + 1 
    wts = (c_double * n)(1, 1, 1) 
    res = (c_double * mm)(0) 
    kks = (c_int * len(kk))(*kk) 
    n = c_int(n) 
    ex = c_double(0) 

    libc.cprogram.restype = None 

    libc.cprogram(wts, res, kks, n, ex) 

    print wts[1] 

Questo funziona, e stampa 1.0.


E se la vostra funzione C fatto restituire un array di struct CONTEXT corrispondente alla vostra ctypes dichiarazione, questo sarebbe tutto bene il lavoro. Per esempio:

#include <stdlib.h> 

typedef struct { 
    double *wts; 
    double res; 
    int kks; 
    int n; 
    double ex; 
} CONTEXT; 

CONTEXT *cprogram(double *wts, double *res, int *kks, int n, double *ex) { 
    int m; 
    m=n+1; 
    res[0]=1.0; 
    kks[0]=1.0; 

    CONTEXT *contexts = malloc(sizeof(CONTEXT) * 4); 
    for (int i=0; i!=4; ++i) { 
    double *wtsses = malloc(sizeof(double) * 5); 
    for (int j=0; j!=4; ++j) { 
     wtsses[j] = i + j; 
    } 
    CONTEXT context = { wtsses, *res, *kks, m, *ex }; 
    contexts[i] = context; 
    } 
    return contexts; 
} 

compilare questo, eseguire lo script Python esistente con l'aggiunta di un print tmp[1].wts[1], e stamperà 2.0.


Infine, per il follow-up, prima cambiamo il codice C per prendere int *n, dove *n è l'ingresso:

void cprogram(double *wts, double *res, int *kks, int *n, double *ex) { 
    int m; 
    m=*n+1; 
    res[0]=1.0; 
    kks[0]=1.0;} 

Ora, chiamare questo da Python, è necessario creare un c_int e passare un puntatore ad esso.Dal momento che si sta già facendo il primo tempo (che non era necessaria prima-basta impostare argtypes invece ... ma è necessaria ora, che è conveniente), è solo un cambiamento di una sola riga:

libc.cprogram(wts, res, kks, pointer(n), ex) 

Questo anche opere se n è un parametro in-out.


Ma in realtà, non è necessario vedere l'oggetto puntatore dal Python; l'unica cosa che stai facendo è crearlo per passare alla funzione, quindi lasciarla raccogliere. Per passare un puntatore C senza creare un oggetto puntatore ctypes (che funziona ancora una volta, anche se n è un parametro in-out), utilizzare byref:

libc.cprogram(wts, res, kks, byref(n), ex) 
+0

Grazie per mettere nel tempo e sforzo per spiegare e correggere i miei errori ! L'unica cosa che ho trovato è che 'c_void' non sembra esistere ma' None' ha funzionato al suo posto. Non ne so molto C quindi sto cercando di evitare di alterare quel codice. – brebs

+0

Seguito rapido: come potrei fare per renderlo tale che la funzione C prendesse n come un puntatore '* n' e poi operasse su di esso facendo' m = * n + 1'? Il mio codice attuale fallisce. In particolare, c'è una soluzione rapida nel codice Python per fare questo? – brebs

+0

@brebs: Giusto, mi dispiace, 'void *' è 'c_void_p', ma' void' è solo 'None'. Ad ogni modo, per il follow-up, modificherò la risposta. – abarnert

Problemi correlati