2009-08-04 21 views
8

Ho sviluppato una DLL per un driver in C. Ho scritto un programma di test in C++ e la DLL funziona correttamente.strutture ricorsive di tipo ctype

Ora mi piacerebbe interagire con questa DLL usando Python. Ho nascosto con successo la maggior parte delle strutture C definite dall'utente, ma c'è un punto in cui devo usare le strutture C. Sono piuttosto nuovo in Python quindi potrei sbagliare.

Il mio approccio è ridefinire alcune strutture in python usando ctype e poi passare la variabile alla mia DLL. Tuttavia, in questi classe ho una lista collegata personalizzato che contiene tipi ricorsivi come seguire

class EthercatDatagram(Structure): 
    _fields_ = [("header", EthercatDatagramHeader), 
       ("packet_data_length", c_int), 
       ("packet_data", c_char_p), 
       ("work_count", c_ushort), 
       ("next_command", EthercatDatagram)] 

Questo non riesce, perché EthercatDatagram all'interno, EthercatDatagram non è già definito in modo che il parser restituisce un errore.

Come dovrei rappresentare questo elenco collegato in python in modo che la mia DLL lo capisca correttamente?

risposta

15

Quasi sicuramente si desidera dichiarare next_command come puntatore. Avere una struttura che contiene se stessa non è possibile (in qualsiasi lingua).

Credo che questo sia ciò che si vuole:

class EthercatDatagram(Structure): 
    pass 
EthercatDatagram._fields_ = [ 
    ("header", EthercatDatagramHeader), 
    ("packet_data_length", c_int), 
    ("packet_data", c_char_p), 
    ("work_count", c_ushort), 
    ("next_command", POINTER(EthercatDatagram))] 
+1

"non è possibile (in qualsiasi lingua)" - questo è vero per le classi o le classi c/C++/java, ma nelle lingue con tipi di dati algebrici non è solo possibile ma anche molto comune. per esempio. 'Elenco dati a = Cons a (Elenco a) | Nil' nelle lingue della famiglia ML (ocaml/haskell/SML/etc). – sinelaw

-1

È necessario accedere a _fields_ staticamente dopo averlo creato.

+1

Questo non funziona. Compila ed esegue, ma provando a utilizzare effettivamente un'istanza della classe dà un errore: AttributeError: L'oggetto 'EthercatDatagram' non ha attributo 'next_command' – user9876

+0

La risposta corretta è di seguito, come postata dall'utente user9876 Devi prima dichiarare la classe con 'pass', quindi dichiarare i campi in una seconda chiamata. Pensalo come una dichiarazione anticipata. – bpescatore

0

Il motivo per cui

EthercatDatagram._fields_.append(("next_command", EthercatDatagram)) 

non funziona è che la macchina che crea gli oggetti descrittori (vedere la fonte della funzione PyCStructType_setattro) per l'accesso l'attributo next_command è attivato solo su assegnazione all'attributo _fields_ della classe. Un semplice accodamento del nuovo campo alla lista passa completamente inosservato.

Per evitare questo errore, utilizzare sempre una tupla (e non un elenco) come valore dell'attributo _fields_: ciò renderà chiaro che è necessario assegnare un nuovo valore all'attributo e non modificarlo in posizione.