Domanda

Aggiornamento: problema risolto! Vedi il fondo del post

Devo consentire agli sviluppatori di Python di passare una serie di dati impaccati (in questo caso vertici) nella mia API, che è una serie di interfacce C ++ esposte manualmente attraverso l'API Python C. La mia impressione iniziale con questo è utilizzare la classe di struttura CTypes per consentire un'interfaccia come questa:

class Vertex(Structure):
_fields_ = [
    ('x', c_float),
    ('y', c_float),
    ('z', c_float),
    ('u', c_float),
    ('v', c_float),
    ('color', c_int)
] 

verts = (Vertex * 3)()
verts[0] = Vertex(0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts[1] = Vertex(0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts[2] = Vertex(-0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)

device.ReadVertices(verts, 3) # This is the interfaces to the C++ object

Dove la funzione a cui sto cercando di passare ha la seguente firma:

void Device::ReadVertices(Vertex* verts, int count);

E l'involucro Python assomiglia a questo:

static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
    PyObject* py_verts;
    int count;

    if(!PyArg_ParseTuple(args, "Oi", &py_verts, &count)) 
        return NULL;

    // This Doesn't Work!
    Vertex* verts = static_cast<Vertex*>(PyCObject_AsVoidPtr(py_verts));

    self->device->ReadVertices(verts, count);

    Py_RETURN_NONE;
}

Naturalmente, il problema più grande che ho è questo: posso recuperare il pyobject per la struttura, ma non ho idea di come lo lancerei al tipo corretto. Il codice di cui sopra fallisce miseramente. Quindi, come dovrei esattamente permettere all'utente di passarmi questo tipo di dati da Python?

Ora, un paio di cose da considerare: prima è che ho già scritto un bel po 'del mio strato Python/C ++ e ne sono perfettamente contento (mi sono allontanato da SWIG in modo da poter avere una maggiore flessibilità). Non voglio ricostruirlo, quindi preferirei una soluzione che funziona con l'api in modo nativo. In secondo luogo, intendo che la struttura del vertice sia pre-definita nel mio codice C ++, quindi preferirei non avere l'utente di doverlo ridimensionare nel Python (riduce gli errori in quel modo), ma io sono Non sono sicuro di come esporre una struttura contigua del genere. In terzo luogo, non ho motivo di provare la struttura di Ctypes oltre a non sapere un altro modo per farlo. Eventuali suggerimenti sono benvenuti. Infine, poiché questo è (come avrai intuito) per un'app grafica preferirei un metodo più veloce rispetto a uno conveniente, anche se il metodo più veloce richiede un po 'più di lavoro.

Grazie per qualsiasi aiuto! Mi sento ancora in giro per le estensioni di Python, quindi è di grande aiuto ottenere input della comunità su alcune delle parti più appiccicose.

SOLUZIONE

Quindi prima di tutto, grazie a tutti coloro che hanno inserito le loro idee. Sono stati un sacco di piccoli bocconcini che si sono aggiunti all'eventuale risposta. Alla fine ecco quello che ho trovato: il suggerimento di Sam di usare struct.pack ha finito per essere proprio sul denaro. Vedendo che sto usando Python 3, ho dovuto modificarlo così leggermente, ma quando tutto è stato detto e fatto, questo ha effettivamente fatto un triangolo sul mio schermo:

verts = bytes()
verts += struct.pack("fffffI", 0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts += struct.pack("fffffI", 0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts += struct.pack("fffffI", -0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)

device.ReadVertices(verts, 3)

Con la mia tupla analizzazione ora sembra così:

static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
    void* py_verts;
    int len, count;

    if(!PyArg_ParseTuple(args, "y#i", &py_verts, &len, &count)) 
        return NULL;

    // Works now!
    Vertex* verts = static_cast<Vertex*>(py_verts);

    self->device->ReadVertices(verts, count);

    Py_RETURN_NONE;
}

Nota che anche se non uso il len Variabile in questo esempio (anche se lo farò nel prodotto finale) devo analizzare la tupla usando "y#" invece di solo "y" o altrimenti si fermerà al primo null (secondo la documentazione). Anche da considerare: i cast void* come questo sono abbastanza pericolosi, quindi per favore fa carichi più controlli degli errori di quello che mostro qui!

Quindi, lavoro ben fatto, buon giorno, vai a casa, sì?

Attesa! Non così in fretta! C'è più!

Sentendomi bene su come tutto ciò che ha funzionato ho deciso, per capriccio, per vedere se il mio precedente tentativo è ancora esploso su di me e tornavo al primo frammento di Python in questo post. (Usando il nuovo codice C, ovviamente) e ... ha funzionato! I risultati erano identici alla versione struct.pack! Oh!

Quindi questo significa che i tuoi utenti hanno una scelta su come forniranno questo tipo di dati e il tuo codice può gestire senza modifiche. Personalmente incoraggerò il metodo Ctype.structure, dal momento che penso che sia una leggibilità più facile, ma in realtà è qualunque cosa l'utente sia a suo agio. (Diamine, potrebbero digitare manualmente una serie di byte in Hex se lo volessero. Funziona. Ho provato.)

Onestamente penso che questo sia il miglior risultato possibile, quindi sono estatico. Grazie ancora e buona fortuna a chiunque altro si imbatte in questo problema!

È stato utile?

Soluzione

Non testato ma dovresti provarlo e farci sapere se è abbastanza veloce per le tue esigenze.

Sul lato Python, imballare i vertici in una stringa anziché un oggetto.

str = "" # byte stream for encoding data
str += struct.pack("5f i", vert1.x, vert1.y, vert1.z, vert1.u, vert1.v, vert1.color) # 5 floats and an int
# same for other vertices

device. ReadVertices( verts, 3) # send vertices to C library

Sul wrapper della libreria C/Python, modifica il tuo pyargs_parsetuple per utilizzare la stringa di formato "si". Questo convertirà la tua stringa Python in una stringa C (char*) che è possibile digitare come puntatore alla struttura vettoriale. A questo punto la stringa C è un flusso di byte/parole/galleggianti e dovrebbe essere quello che stai cercando.

Buona fortuna!

Altri suggerimenti

La cosa più semplice che posso vedere sarebbe solo evitare del tutto il problema ed esporre un dispositivo_readvertex che assume x, y, z, u, v e colore come argomenti. Ciò ha evidenti svantaggi, come far dar da mangiare ai programmatori di Python uno per uno.

Se questo non è abbastanza buono (sembra probabile che non lo sia), allora potresti provare a definire un nuovo tipo Python come descritto qui. È un po 'più di codice, ma penso che questo sia il metodo "più architettonicamente solido", perché assicurati che i tuoi sviluppatori Python utilizzino la stessa definizione di tipo che si trova nel codice C. Permette anche un po 'più di flessibilità di una struttura semplice (è davvero una classe, con il potenziale per aggiungere metodi, ecc.), Di cui non sono sicuro di aver effettivamente bisogno, ma potrebbe tornare utile in seguito.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top