curioso2aprender
Tengo una función en mi biblioteca C, digamos runsim()
que lleva el puntero a struct repdata
como uno de los argumentos, donde struct repdata
es dado por
struct repdata {
int *var1;
int *var2;
int *var3;
char *var4;
double *var5;
double *var6;
int *var7;
};
Cuando uso C exclusivamente, inicializo una variable de tipo struct repdata
llamando a la función,
struct repdata data;
void create_data_container(struct repdata *data, int len_data)
{
data -> var1 = malloc( sizeof(int) * len_data );
data -> var2 = malloc( sizeof(int) * len_data );
data -> var3 = malloc( sizeof(int) * len_data );
data -> var4 = malloc( sizeof(char) * len_data );
data -> var5 = malloc( sizeof(double) * len_data);
data -> var6 = malloc( sizeof(double) * len_data);
data -> var7 = malloc( sizeof(int) * len_data);
}
y luego complete esta estructura a medida que avanza la simulación. Después de haber escrito los datos en un archivo, libero la memoria usando el estándar
free(data.var1);
free(data.var2);
.
.
.
free(data.var7);
quiero llamar al runsim()
función de Python usando Python Ctypes. Para esto necesito pasar un puntero a una variable (que es equivalente al tipo de struct repdata
) como uno de los runsim()
argumentos Supongamos que en Python he definido un equivalente de struct repdata
en la siguiente manera.
import ctypes as C
class Repdata(C.Structure):
_fields_ = [
("var1", C.POINTER(C.c_int)),
("var2", C.POINTER(C.c_int)),
("var3", C.POINTER(C.c_int)),
("var4", C.POINTER(C.c_char)),
("var5", C.POINTER(C.c_double)),
("var6", C.POINTER(C.c_double)),
("var7", C.POINTER(C.c_int)),
]
¿Cuál es el equivalente de create_data_container
función que se muestra arriba en el lado de Python? Quiero inicializar una instancia de Repdata que se pueda pasar al código C y tenga suficiente memoria para almacenar los datos de replicación. Y, una vez que se completa la simulación, ¿cómo libero la memoria de Python?
Estoy usando Ubuntu Linux 12.04.
Gracias de antemano por tu ayuda.
marca tolonen
Puede asignar búferes usando ctypes
y asignarlos a los punteros. Una vez que los objetos ctypes de Python no tengan referencias, se liberarán automáticamente. Aquí hay un ejemplo simple (con una DLL de Windows… no tengo una máquina Linux a mano, pero la idea es la misma) y un envoltorio de Python.
create_string_buffer
asigna un búfer grabable que se puede pasar de Python a C que ctypes
se organizará como un char*
.
También puede crear matrices escribibles de ctypes
tipos con la sintaxis:
variable_name = (ctypes_type * length)(initial_values)
xh
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
struct example {
char* data;
int len; // of data buffer
double* doubles;
int count; // of doubles
};
DLL_API void func(struct example* p);
xc
#include <stdio.h>
#define DLL_EXPORTS
#include "x.h"
void func(struct example* p)
{
int i;
strcpy_s(p->data,p->len,"hello, world!");
for(i = 0; i < p->count; i++)
p->doubles[i] = 1.1 * (i + 1);
}
x.py
import ctypes
class Example(ctypes.Structure):
_fields_ = [
('data',ctypes.POINTER(ctypes.c_char)),
('len',ctypes.c_int),
('doubles',ctypes.POINTER(ctypes.c_double)),
('count',ctypes.c_int)]
def __init__(self,length,count):
self.data = ctypes.cast(ctypes.create_string_buffer(length),ctypes.POINTER(ctypes.c_char))
self.len = length
self.doubles = (ctypes.c_double * count)()
self.count = count
def __repr__(self):
return 'Example({},[{}])'.format(
ctypes.string_at(self.data),
','.join(str(self.doubles[i]) for i in range(self.count)))
class Dll:
def __init__(self):
self.dll = ctypes.CDLL('x')
self.dll.func.argtypes = [ctypes.POINTER(Example)]
self.dll.func.restype = None
def func(self,ex):
self.dll.func(ctypes.byref(ex))
d = Dll()
e = Example(20,5)
print('before:',e)
d.func(e)
print ('after:',e)
Producción
before: Example(b'',[0.0,0.0,0.0,0.0,0.0])
after: Example(b'hello, world!',[1.1,2.2,3.3000000000000003,4.4,5.5])
-
Puedo estar equivocado, pero esto parece ser diferente de lo que estoy buscando. En mi problema, los datos se generan en el código C y quiero pasar un contenedor al código C de Python que se puede llenar con esos datos. En la respuesta aquí, los datos se pasan de Python a C.
– Curioso2aprender
8 de septiembre de 2013 a las 3:42
-
@ Curious2learn, los búferes están asignados pero no tienen que inicializarse en el lado de Python. También puede completarlos en el lado C y los datos aparecerán en el lado de Python. Cambiaré lo anterior para demostrar eso en su lugar.
–Mark Tolonen
8 de septiembre de 2013 a las 5:11
-
@MarkTolonen Encontrando su respuesta: ¿podría “inyectar”
create_string_buffer
como unmalloc
reemplazo en mi c-biblioteca a través deCFUNCTYPE(c_void_p, c_size_t)(create_string_buffer)
? Me gustaría evitar “preguntarle” a mi biblioteca c cuánta memoria necesita todo el tiempo…– sebastián
14/11/2014 a las 15:20