Cython – Implementierung von Rückrufen

Ich habe mit Cython in einem Versuch, eine Schnittstelle mit einer Bibliothek in c ++ geschrieben zu arbeiten. So weit geht es ziemlich gut, und ich kann effektiv MOST-Funktionen in der Bibliothek verwenden. Mein einziges Problem liegt bei der Umsetzung von Rückrufen. Die Bibliothek verfügt über 4 Funktionsdefinitionen, die so etwas aussehen:

typedef void (*Function1)(const uint16_t *data, unsigned width, unsigned height); void SetCallBack(Function1); 

Also um sie zu implementieren, dachte ich, ich würde so etwas mit Cython machen:

 ctypedef void (*Function1)(unsigned short *data, unsigned width, unsigned height); cdef extern from "lib.hpp": void SetCallBack(Function1) 

Was aber eigentlich richtig kompiliert ist, kann ich aber nicht für das Leben von mir denken, wie man das eigentlich so umsetzen kann, dass der Rückruf funktionieren würde. Ich habe zuerst versucht, eine Funktion zu schaffen, die das einfach nennen würde, ähnlich wie du es für irgendeine andere Funktion tun würdest.

 def PySetCallBack(Func): SetCallBack(Func) 

Aber das gibt mir den (vorhersagbaren) Fehler:

"Python-Objekt kann nicht in 'Function1' umwandeln

Ja ja, das ist, wo ich bin. Wenn jemand irgendwelche Erfahrung bei der Einrichtung von Callbacks in Cython hat, wäre ich für jede Hilfe sehr dankbar. Vielen Dank.

Bearbeiten : Nach deinem Ratschlag habe ich eine Zwischenfunktion mit einem Cdef erstellt, das wie folgt aussieht:

 cdef void cSetCallBack(Function1 function): SetCallBack(function) 

Das scheint mich zu haben … näher? Jetzt einen anderen Fehler bekommen:

 error: invalid conversion from 'void (*)(short unsigned int*, unsigned int, unsigned int)' to 'void (*)(const uint16_t*, unsigned int, unsigned int)' 

Nun, so weit ich kann sagen, diese Typen sind identisch, so kann ich nicht, was los ist.

Edit2 : Dieses Problem wurde behoben , indem ich ein neues typedef erklärte:

 ctypedef unsigned short uint16_t 

Und das als das Argument zu nennen, aber anscheinend war das eigentlich gar nicht näher, aber ich nehme mich nur um eine Seitenspur, denn beim Versuch, diese Funktion aufzurufen, bekomme ich das gleiche "Kann das Python-Objekt nicht auf 'Function1' "Fehler immer wieder.

Also, ich bin ziemlich viel zurück, wo ich angefangen habe. Nur das, was ich jetzt machen kann, ist ausdrücklich das Pythonobjekt zu sehen, das als ac-Funktion kommt, aber um ehrlich zu sein, habe ich keine Ahnung, wie ich das machen würde.

Bearbeiten Sie die dritte : Okay, nach der Sektion Ihrer Antwort bekomme ich endlich, und es funktioniert, so hooray und was nicht. Was ich am Ende tat, war die Schaffung einer Funktion wie folgt:

 cdef void cSetCallback(Function1 function): SetCallback(function) cdef void callcallback(const_ushort *data, unsigned width, unsigned height): global callbackfunc callbackfunc(data,width,height) cSetCallback(callcallback) def PySetCallback(callbackFunc): global callbackfunc callbackfunc = callbackFunc 

Also jetzt das einzige Problem ist, dass es const_ushort * Daten nicht in ein Pythonobjekt umwandeln kann, aber das ist ein anderes Problem ganz, also denke ich, das ist ein Dankeschön.

  • Tutorials zur Optimierung nicht-trivialer Python-Anwendungen mit C-Erweiterungen oder Cython
  • Pickle Cython Klasse
  • Kombinieren von Cython mit MKL
  • Cython installieren Problem
  • Wie beschleunige man Pandas multilevel dataframe sum?
  • Kann Cython zu einer EXE kompilieren?
  • Kann ich die Cython-Module statisch in eine ausführbare Datei verknüpfen, die Python einbettet?
  • Wrapping / Casting C-Struktur in Cython-Python-Klasse
  • 2 Solutions collect form web for “Cython – Implementierung von Rückrufen”

    Wenn Sie die Bibliothek ändern können, um zu definieren:

     typedef void (*Function1)(const uint16_t *data, unsigned width, unsigned height, void *user_data); void SetCallBack(Function1, void*); 

    Stattdessen fürchte ich, dass du kein Glück hast. Wenn du die void* hast, dann definierst du eine Funktion, die ein SetCallBack Objekt mit korrekten Argumenten und SetCallBack mit dieser Funktion SetCallBack und die SetCallBack .

    Wenn du es nicht kannst, aber der Rückruf ist global (es scheint zu sein), kannst du eine globale Variable erstellen, um das SetCallBack zu speichern. Als du wieder eine Funktion zum Aufruf des SetCallBack und an SetCallBack Ihr PySetCallback würde einfach die globale und die ordnungsgemäße Funktion ist registriert.

    Wenn der Rückruf kontextspezifisch ist, aber Sie haben keine Möglichkeit, ihm einen "User Data" Zeiger zu übergeben, ich fürchte, Sie haben kein Glück hier.

    Ich kenne Python und C ++, aber nicht Cython, also weiß ich nicht, ob du die Funktion in Cython erstellen kannst oder ob du in C ++ schreiben musst.

    Ich war vor kurzem in der Situation, wo ich auch eine bestehende C ++ – Bibliothek mit Python mit Cython verbinden musste, um einen intensiven Einsatz von Events / Callbacks zu machen. Es war nicht so leicht, Quellen darüber zu finden und ich möchte das alles hier zusammensetzen:

    Zuerst einmal die C ++ – Rückrufklasse (basierend auf 'double ( METHOD) (void )' Prototyp, aber es könnte templatisiert worden sein, da Cython Vorlagen behandeln kann):

    ALabCallBack.h:

     #ifndef ALABCALLBACK_H_ #define ALABCALLBACK_H_ #include <iostream> using namespace std; namespace elps { //template < typename ReturnType, typename Parameter > class ALabCallBack { public: typedef double (*Method)(void *param, void *user_data); ALabCallBack(); ALabCallBack(Method method, void *user_data); virtual ~ALabCallBack(); double cy_execute(void *parameter); bool IsCythonCall() { return is_cy_call; } protected: bool is_cy_call; private: //void *_param; Method _method; void *_user_data; }; } /* namespace elps */ #endif /* ALABCALLBACK_H_ */ 

    ALabCallBack.cpp:

     #include "ALabCallBack.h" namespace elps { ALabCallBack::ALabCallBack() { is_cy_call = true; }; ALabCallBack::~ALabCallBack() { }; ALabCallBack::ALabCallBack(Method method, void *user_data) { is_cy_call = true; _method = method; _user_data = user_data; }; double ALabCallBack::cy_execute(void *parameter) { return _method(parameter, _user_data); }; } /* namespace elps */ 

    Woher :

    • 'Callback' :: Die Muster / Konverter-Methode, um eine Python (= Methode) Objektmethode aus C-typisierten infos zu löschen

    • 'Method' :: Die effektive Methode, die vom Python-Benutzer (= user_data)

    • 'Parameter' :: Der Parameter, der an die 'Methode' übergeben werden soll

    Jetzt müssen wir die .pyx-Datei implementieren …

    Unser Basisprototyp:

     ctypedef double (*Method)(void *param, void *user_data) 

    Dann stellen wir einen Cython-Wrapper für die C ++ – Klasse zur Verfügung:

     cdef extern from "../inc/ALabCallBack.h" namespace "elps" : cdef cppclass ALabCallBack: ALabCallBack(Method method, void *user_data) double cy_execute(void *parameter) 

    Die Muster / Konverter-Methode für die Übersetzung von C typisierten Prototypen zu einem Python-Objektaufruf verwendet werden:

     cdef double callback(void *parameter, void *method): return (<object>method)(<object>parameter) 

    Jetzt lassen wir diese Features in eine Cython-Klasse einbetten:

     cdef class PyLabCallBack: cdef ALabCallBack* thisptr def __cinit__(self, method): # 'callback' :: The pattern/converter method to fire a Python # object method from C typed infos # 'method' :: The effective method passed by the Python user self.thisptr = new ALabCallBack(callback, <void*>method) def __dealloc__(self): if self.thisptr: del self.thisptr cpdef double execute(self, parameter): # 'parameter' :: The parameter to be passed to the 'method' return self.thisptr.cy_execute(<void*>parameter) 

    Bearbeiten: Bessere Eingabe für Execute-Funktion: def execute => cpdef double

    Das ist es. Nennen Sie es so etwas wie das:

     def func(obj): print obj obj.Test() # Call to a specific method from class 'PyLabNode' return obj.d_prop n = PyLabNode() # Custom class of my own cb = PyLabCallBack(func) print cb.execute(n) 

    Da Python implizit eingegeben wird, können wir auf die Eigenschaften des 'obj'-Objekts zugreifen, das sich auf die Klasse des Objekts bezieht, das als Argument übergeben wird, wenn die Zeit zum Abrufen des Rückrufs kommt.

    Es kann ganz einfach für die reine C-Implementierung angepasst werden. Bitte sagen Sie mir, wenn Sie eine mögliche Erweiterung dafür sehen können (in meinem Fall sind Perfs sehr wichtig, da die Ereignisse intensiv gefeuert werden).

    Python ist die beste Programmiersprache der Welt.