2014-07-08 15 views
7

Sto provando a chiamare un codice c generato dal codificatore Matlab. Matlab usa una struttura c chiamata emxArray per rappresentare le matrici (documentata qui: http://www.mathworks.co.uk/help/fixedpoint/ug/c-code-interface-for-unbounded-arrays-and-structure-fields.html).Definizione ctypes Python per c struct

struct emxArray_real_T 
{ 
    double *data; 
    int *size; 
    int allocatedSize; 
    int numDimensions; 
    boolean_T canFreeData; 
}; 

ho poco ctypes esperienza e sto lottando per creare una struct equivalente che posso utilizzare per passare vettori avanti e indietro per le funzioni definite nel c .so

qui è dove ho ottenuto finora in pitone ...

class EmxArray(ctypes.Structure): 
    """ creates a struct to match emxArray_real_T """ 

    _fields_ = [('data', ctypes.POINTER(ctypes.c_double)), 
       ('size', ctypes.POINTER(ctypes.c_int)), 
       ('allocatedSize', ctypes.c_int), 
       ('numDimensions', ctypes.c_int), 
       ('canFreeData', ctypes.c_bool)]  

Tuttavia, se mi definiscono questa:

data = (1.1, 1.2, 1.3, 1.4) 
L = len(data) 

x = EmxArray() 
x.data = (ctypes.c_double * L)(*data) 
x.data = (ctypes.c_int * 1)(L)  

questo allora funziona

print len(x.data[:L]) 

for v in x.data[:L]: print v 

Edit: Ho riordinato e adottato il suggerimento di Roland e può estrarre i dati utilizzando

Ho bisogno di indagare ulteriormente per vedere se posso usare con successo questo struct per passare e ricevere dati dal codice c.

Soluzione

Attuazione del struct ctypes come suggerito da Roland non ha funzionato - i valori restituiti erano spazzatura, non ho mai lavorato fuori perché come ho perseguito un'implementazione basata pitone della risposta di lilbil. Ho accettato quella risposta perché era la più vicina ...

Documenterò qui la mia soluzione perché potrebbe salvare qualcun altro perdendo tutto il tempo che ho.

In primo luogo ho generato una semplice funzione MATLAB che moltiplica ciascun elemento di una funzione da solo & utilizzato il codificatore per compilarlo a un c .so. Questo è importato in python usando i ctype. Il codice è il seguente ...

import ctypes 

LIBTEST = '..../dll/emx_test/' 
EMX = ctypes.cdll.LoadLibrary(LIBTEST + 'emx_test.so') 
init = EMX.emx_test_initialize() 

# Create a data structure to hold the pointer generated by emxCreateWrapper... 
class Opaque(ctypes.Structure): 
    pass 

# make some random data to pass in 
data_in = [1., 2., 4., 8., 16.] 
L = len(data_in) 
# create an empty array of the same size for the output 
data_ou = [0] * L 

# put this in a ctypes array 
ina = (ctypes.c_double * L)(*data_in) 
oua = (ctypes.c_double * L)(*data_ou) 
# create a pointer for these arrays & set the rows and columns of the matrix 
inp = ctypes.pointer(ina) 
oup = ctypes.pointer(oua) 

nrows = ctypes.c_int(1) 
ncols = ctypes.c_int(L) 

# use EMX.emxCreateWrapper_real_T(double *data, int rows, int cols) to generate an emx wrapping the data 
# input arg types are a pointer to the data NOTE its not great to have to resize the ctypes.c_double but cant see another way 
EMX.emxCreateWrapper_real_T.argtypes = [ctypes.POINTER(ctypes.c_double * L), ctypes.c_int, ctypes.c_int] 
# a pointer to the emxArray is returned and stored in Opaque 
EMX.emxCreateWrapper_real_T.restype = ctypes.POINTER(Opaque) 
# use emxCreateWrapper 
in_emx = EMX.emxCreateWrapper_real_T(inp, nrows, ncols) 
ou_emx = EMX.emxCreateWrapper_real_T(oup, nrows, ncols) 

# so now we have to emx's created and have pointers to them we can run the emx_test 
# emx test looks like this in matlab 
# 
# function res = emx_test (in) 
#  res = in .* in; 
# end 
# 
# so basically it multiplies each element of the matrix by itself 
# 
# therefore [1., 2., 4., 8., 16.] should become [1., 4., 8., 64., 256.] 

EMX.emx_test(in_emx, ou_emx) 

# and voila...that's what we get 
print 'In: ', ina[:L] 
print 'Out:', oua[:L] 

uscita:

In: [1.0, 2.0, 4.0, 8.0, 16.0] 
Out:[1.0, 4.0, 16.0, 64.0, 256.0] 

Grazie a tutti per il vostro tempo & suggerimenti.

+0

Mi sembra che probabilmente non sono necessarie le parti 'sz' e' ... * sz)) 'della classe 'EmxArray'. –

+0

La ragione per cui l'ho fatto è che i dati saranno una matrice sarebbe meglio se potessi consentire comunque un array di dimensioni variabili. –

risposta

2

Non sono esperto con l'interfaccia Python-C, quindi quello che sto suggerendo potrebbe non essere l'ideale. La mia ipotesi è che probabilmente l'incidente è dovuto al fatto che x->data non viene mai inizializzato e la memoria a cui sta puntando non è allocata.

Un approccio che ho seguito durante l'interfaccia con MATLAB Coder ha generato codice da altri linguaggi in presenza di argomenti emxArray è di scrivere a mano una funzione di interfaccia C che fornisce un'API più semplice. Questo allevia l'onere di dover costruire un emxArray nell'altro ambiente (Android Java nel mio caso particolare). Se la funzione generata foo prende e restituisce un 2-D double matrice, quindi qualcosa come il seguente potrebbe funzionare:

void foo(double *x, int *szx, double **y, int *szy); 

Questa funzione richiederebbe un puntatore ai dati di ingresso e le sue dimensioni e fornire un puntatore all'uscita dati e le sue dimensioni.L'implementazione sarebbe simile:

void foo(double *x, int *szx, double **y, int *szy) 
{ 
    emxArray_real_T *pEmx; 
    emxArray_real_T *pEmy; 

    /* Create input emxArray assuming 2-dimensional input */ 
    pEmx = emxCreateWrapper_real_T(x, szx[0], szx[1]); 

    /* Create output emxArray (assumes that the output is not */ 
    /* written before allocation occurs) assuming 2-D output */ 
    pEmy = emxCreateWrapper_real_T(NULL, 0, 0); 

    /* Call generated code (call foobar_initialize/terminate elsewhere) */ 
    foobar(pEmx, pEmy); 

    /* Unpack result - You may want to MALLOC storage in *y and */ 
    /* MEMCPY there alternatively        */ 
    *y = pEmy->data; 
    szy[0] = pEmy->size[0]; 
    szy[1] = pEmy->size[1]; 

    /* Clean up any memory allocated in the emxArrays (e.g. the size vectors) */ 
    emxDestroyArray_real_T(pEmx); 
    emxDestroyArray_real_T(pEmy); 
} 

Si dovrebbe essere in grado di chiamare questa funzione da Python più semplicemente e passare i dati desiderati in base alle esigenze.

My other answer ha ulteriori dettagli sulle funzioni emxArray_* trovate nel file foobar_emxAPI.h.

+0

Grazie per questa risposta completa. Penso che questa sia probabilmente la strada da percorrere per esplorare questa strada oggi. Attualmente sto lavorando a uno script matlab fittizio che prende semplicemente un emx_array e lo restituisce come emx_array. La libreria con cui alla fine devo lavorare definisce molte funzioni e quindi il wrapping è più ingombrante, mi chiedo se sarebbe possibile definire una funzione generica che prende un vettore e definisce un emx_array che può essere passato a una funzione e viceversa. .. –

+0

Questo potrebbe essere possibile. Come differiscono i tipi e le complessità degli argomenti della libreria? Generalmente le differenze possono esistere in tipo, complessità o numero di dimensioni degli argomenti. Un cambiamento in quelli richiederebbe un cambiamento comportamentale nella routine di conversione. La tua routine di conversione può essere scritta in C++? Se è così, potrebbe essere templatizzato con specializzazioni per diversi tipi che sanno come chiamare le funzioni appropriate in 'foo_emxApi.h'. –

+0

Un pensiero alternativo che ho è che potresti essere in grado di dichiarare un puntatore a un tipo 'emxArray' appropriato in Python. Quindi è possibile chiamare le funzioni generate 'emxCreate *' o 'emxCreateWrapper *' per inizializzare quel puntatore dal codice Python. Ciò dovrebbe ridurre al minimo la quantità di C che è necessario scrivere e queste funzioni hanno firme semplici con argomenti numerici. Ciò potrebbe anche consentire di scrivere la funzione di conversione 'emxArray' del vettore <--> in Python. –

5

Basta creare un puntatore, assegnare i dati in seguito;

import ctypes 

class EmxArray(ctypes.Structure): 
    """ creates a struct to match emxArray_real_T """ 

    _fields_ = [('data', ctypes.POINTER(ctypes.c_double)), 
       ('size', ctypes.POINTER(ctypes.c_int)), 
       ('allocatedSize', ctypes.c_int), 
       ('numDimensions', ctypes.c_int), 
       ('canFreeData', ctypes.c_bool)] 

data = (1.3, 3.5, 2.7, 4.1) 
L = len(data) 

e = EmxArray() 
e.data = (ctypes.c_double * L)(*data) 
e.size = (ctypes.c_int * 1)(L) 
# et cetera