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)
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
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
@brebs: Giusto, mi dispiace, 'void *' è 'c_void_p', ma' void' è solo 'None'. Ad ogni modo, per il follow-up, modificherò la risposta. – abarnert