2016-06-23 4 views
10

Ich schreibe eine High-Level-Schnittstelle für eine C-Bibliothek für Python mit Cython.
Ich habe eine Erweiterung Typ A, die die Bibliothek mit einem Zeiger auf eine komplexere C-Kontextstruktur c_context initialisiert. Der Zeiger wird in A gespeichert.
A hat auch eine def Funktion, die wiederum eine andere Erweiterung Typ B Initialisierung einer anderen C-Struktur mit einer Bibliothek Funktionsaufruf erstellt. Diese Struktur wird für die nachfolgenden Bibliotheksaufrufe in B benötigt.
B braucht die c_context Zeiger von A, die von mir in der Verlängerung Typ py_context um es B-__cinit__ zu passieren gewickelt ist:Wie schreibe ich einen vollständigen Python-Wrapper um ein C-Struct mit Cython?

#lib.pxd (C library definitions) 
cdef extern from "lib.h": 
    ctypedef struct c_context: 
     pass 

#file py_context.pxd 
from lib cimport c_context 

cdef class py_context: 
    cdef c_context *context 
    cdef create(cls, c_context *context) 
    cdef c_context* get(self) 

#file py_context.pyx 
def class py_context: 
    @staticmethod 
    cdef create(cls, c_context *c): 
     cls = py_nfc_context() 
     cls.context = c 
     return cls 

    cdef c_context* get(self): 
     return self.context 

die Wrapper Pass mit dem richtigen C Kontext funktioniert perfekt.

Jetzt muss ich die C-Struktur aus py_context wieder und speichern Sie es in B. Ich fügte cdef c_context get(self) zu py_context.pxd/pyx hinzu. py_context.get() von Bs Aufruf __cinit__ Ergebnisse in: AttributeError: py_context object has no attribute get.

Es scheint, wie ich meinen Kopf nicht um, wenn cdef Funktionen in Cython nennen.

Also meine Frage ist: Was ist der beste Weg, um die C-Struktur wieder aus meiner Wrapper-Klasse zu extrahieren?

+0

Haben Sie das gelesen? http://docs.cython.org/src/tutorial/clibraries.html – chrisb

Antwort

6

Das Problem ist, dass Cython nicht den Datentyp Ihrer py_context Variable zur Kompilierzeit kennt. Aufrufe an cdef Funktionen werden zur Kompilierungszeit aufgelöst und es gibt keinen Mechanismus, um es zur Laufzeit durch Attributsuche (wie mit normalen Python-Funktionen) herauszufinden.

[Beachten Sie, dass def Funktionen innerhalb Cython geschrieben noch kompiliert werden und Datentypen festlegen, so durchaus in der Lage sind cdef Funktionen aufrufen, wenn sie die richtigen Informationen haben.]

Sie geben nicht den entsprechenden Code in dem es wird falsch (der Konstruktor vom Typ B), aber hier ist ein sehr vereinfachtes Beispiel, das Ihnen hoffentlich ein paar Möglichkeiten geben, um es zu beheben:

cdef class A: 
    cdef f(self): 
     return 

def f1(var): 
    var.f() 

#f1(A()) # will fail at runtime with an attribute error 

In f1 die Art der var ist unbekannt, und daher kann man rufen Sie cdef Funktionen nicht an.

def f2(A var): 
    var.f() 

f2(A()) # will work 
f2(1) # will fail, int can't be converted to A 

In f2 die Art der var gezwungen ist, A zu sein, und deshalb kann es glücklich mit Acdef verbundenen Funktionen aufrufen. Wenn Sie etwas übergeben, das nicht A ist, erhalten Sie eine TypeError zur Laufzeit.

def f3(var): 
    cdef A another_reference_to_var = var # this does test that the types match 
    another_reference_to_var.f() 

f3(A()) # will work 
f3(1) # will fail, int can't be converted to A 

Die Funktion f3 kann eine Variable beliebigen Typs annehmen.Wenn Sie es jedoch another_reference_to_var zuweisen, also cdef als A, wird überprüft, ob der Typ übereinstimmt (und eine Laufzeitausnahme ausgelöst wird, wenn dies nicht der Fall ist). Da another_reference_to_var zur Kompilierungszeit als A bekannt ist, können Sie A s cdef Funktionen aufrufen.

Im Wesentlichen müssen Sie den Typ des relevanten Eingangs für Ihre __cinit__-Funktion angeben.

+2

+1 Dies ist eine bessere Antwort, also entfernte ich sofort meine, da es ein bisschen irreführend war. [Hier] (https://github.com/cythonbook/examples/tree/master/07-wrapping-c/02-wrapping-c-structs-mt-random) ist eine URL mit einem vollständigen Beispiel für das Umbrechen einer Struktur OP um zu sehen, ob er braucht. –

+0

Funktioniert wie ein Charme. Die fehlende Typinformation in Bs '__cinit__' war das Problem. Das Hinzufügen von 'py_context' löst das Problem. Ihre Informationen sollten den Cython-Dokumenten hinzugefügt werden, da sie manchmal etwas vage sind. –