2012-02-24 23 views
6

Hay una libx.so que exportan 2 funciones, y una struct,C struct en 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); 
} 

Quiero llamar create en Python y guarde el Tag *res devuelto por create, más adelante llamaré use y pase la Tag *res guardado antes de use, aquí es que (sólo para demostrar):

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

el código anterior podría ser malo, sólo para demostrar lo que quiero que hacer.

Y mi problema es que, ¿cómo podría guardar el resultado devuelto por create? Debido a que devuelve un puntero a un struct definido por el usuario, y no quiero construir la contraparte de struct Tag en Python, ¿sería el c_void_p el truco?

ACTUALIZACIÓN

de @ respuesta de David, yo todavía no entiendo una cosa:

el puntero (c_char_p("a")) sólo es válido para la duración de la llamada a create. Tan pronto como cree devoluciones, ese puntero no es más válido.

Y asigno a c_char_p("a")t->name en create, cuando la llamada a create acabados, es t->name una referencia colgante? Porque según las palabras entrecomilladas, ese puntero ya no es válido después de create. ¿Por qué c_char_p("a") ya no es válido?

Respuesta

4

El código C que presenta simplemente no va a funcionar. Debe ser mucho más preciso sobre qué parte asigna y es responsable de la memoria del montón.

En su ejemplo actual, pase c_char_p("a") al código C. Sin embargo, el puntero a esa memoria ctypes solo es válido durante la duración de la llamada al create. Tan pronto como regrese create, ese puntero ya no será válido. Pero tomó una copia del puntero dentro de create. Por lo tanto, es posible que falle la llamada posterior al use.

Necesitará tomar una copia del contenido de esa cadena y almacenarla en la estructura. Si lo hace, puede usar libx.create.restype = c_void_p de forma segura.

Pero si quiere que la memoria asignada sea desasignada, deberá proporcionar una función destroy para que coincida con la función create. Con estos cambios el código C se vería así:

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); 
} 

código Python se vería así:

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

me puse mal en mis pantalones –

+0

Bueno, señor, hay 3 cosas que no entiendo. 1, 'el puntero a esa memoria de ctypes solo es válido durante la llamada a crear. ¿Por qué? Supongo que es porque después de la llamada a 'create',' c_char_p ("a") ''s ref-count va a cero, por lo que' "a" 'es basura, ¿no? 2, en su código, 'res' es un objeto' c_void_p', pero 'libx.use' toma' Tag * 'arg, ¿puedo pasar' res' directamente a 'libx.use' sin ningún molde? 3, ¿por qué debería 'destruir (res) 'explícitamente? – Alcott

+0

Solo corro una prueba, parece que mi código puede funcionar correctamente, señor. – Alcott

0

Python hace el recuento de referencias. Deberá usar Py_INCREF() y sus amigos para los objetos que se devuelven de las bibliotecas "externas".

ACTUALIZACIÓN: No sé sobre .so cargando por python, tal vez el método propuesto por @David Hefferman lo hace automágicamente.

UPDATE2: borrarme!

+1

La pregunta se refiere a ctypes que es una interfaz de función foránea, similar a pinvoke si usted sabe eso. 'Py_INCREF' simplemente no es pertinente. –

+0

Ok. Eliminaré – wildplasser