ctypes und Callbacks

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hi,

Ich dachte mir heute, dass ich versuche etwas nicht komplett triviales mit ctypes zu wrappen und habe mich für LibUnique entschieden. Abgesehen davon dass je mehr ich davon sehe, desto schlechter finde ichs geht es eigentlich ganz gut vorran. Ich kann inzwischen feststellen ob bereits eine Instanz/Exemplar der Applikation läuft. Nun würde ich gerne aber noch einen Schritt weiter gehen und Nachrichten umherschicken können.

Dazu habe ich mir das Beispiel von der LibUnique-Seite genommen und es angepasst so dass es immhin irgendwie kompiliert (es funktioniert zwar nicht richtig, aber ausreichend gut, um es auszuprobieren.

Kompiliert wird es mit

Code: Alles auswählen

gcc $(pkg-config --cflags unique-1.0) $(pkg-config --libs unique-1.0) demo.c -o demo
(man muss dazu die libunique-1.0 zusammen mit den Headern etc. installiert haben, ist beim aktuellen Ubuntu und Debian Squeeze dabei, ich hab einfach einen Backport für Lenny erstellt)

Das funktioniert nun ausreichend gut. Mein zusammengehackter Python-Code tut auch relativ gut, bis eben auf die Callbacks.

Im C-Beispiel steht etwa folgendes:

Code: Alles auswählen

g_signal_connect (app, "message-received", G_CALLBACK (message_received_cb), NULL);
``g_signal_connect`` ist ein Makro aus GObject dass intern ``g_signal_connect_data`` aufruft. Das habe ich mal nachgebaut, alledings macht mir der G_CALLBACK-Makroaufruf etwas Sorgen, denn da wird ein Cast der Funktion zu ``GCallback`` gemacht, wo ich nicht recht weiß wie das mit ctypes möglich ist. Den Callback habe ich so definiert wie in der ctypes-Doku, die Typen habe ich aus dem C-Beispiel und sollten eigentlich stimmen.

Schließlich ist es wohl auch so, dass die Messages nur zugestellt werden können, wenn ``gtk.gtk_main()`` aufgerufen wird. Dann Segfaultet es allerdings sofort, wenn eine Nachricht zugestellt werden soll.

Irgendwo ist da noch der Wurm drin und es würde mich freuen wenn mich jemand mir den ein oder anderen Tipp geben würde.

P.S.: Ja, das ist Python 3-Code. Irgendwer muss ja anfangen und da ctypes in der Stdlib ist, bot sich das an.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nichts zu dem Callbacksproblem, aber eine andere Sache:

Statt hardkodierter so-Files würde ich lieber den Namen nutzen, den mir `ctypes.util.find_library()` ausspuckt. Das hat den Vorteil der Plattformunabhängigkeit. `find_library()` funktioniert jetzt sogar unter Windows.
Changed in version 2.6: Windows only: find_library("m") or find_library("c") return the result of a call to find_msvcrt().
Quelle

Leider wurde das offenbar noch nicht in die Doku weiter oben aufgenommen:
On Windows, find_library searches along the system search path, and returns the full pathname, but since there is no predefined naming scheme a call like find_library("c") will fail and return None.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Der Code ist erstmal nur als Proof-of-concept gedacht, wo ich herausfinden will, ob die Lib überhaupt mit ctypes einigermaßen vernünftig wrappbar ist. Natürlich würde ich das ganze etwas sauberer strukturieren, aber das macht ja keinen Sinn wenn ich den Callback nicht hinbekomme.

Windows ist warscheinlich sowieso außen vor, da libunique ein Dependency auf DBus hat und DBus unter Windows relativ unüblich ist. Dort würde man wohl eine andere, weniger umständliche Möglichkeit nutzen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
fred.reichbier
User
Beiträge: 155
Registriert: Freitag 29. Dezember 2006, 18:27

Hallo,

bei mir gibt es schon einen Segfault in gtk_init, das liegt daran, dass du die Funktion ohne Parameter aufrufst. Weils mir momentan zu aufwändig ist, die argc- und argv-Parameter zusammenzufrickeln, hab ich einfach den Aufruf einfach mal mit `gtk.gtk_init(0, 0)` ersetzt, dann hab ich noch die selbstgebaute Endlosschleife durch `gtk.gtk_main()` ersetzt. Jetzt kommt immerhin die Ausgabe "Starting to run". Wenn ich das Skript jetzt nochmal ausführe, segfaultet das erste Skript, das zweite (sendende) sagt "Unique-DBus-WARNING **: Error while sending message: Did not receive a reply.".
Den Segfault gibts bei mir in "unique_marshal_ENUM__INT_BOXED_UINT", also scheint der Fehler beim Aufruf des Callbacks aufzutreten.
Die Signatur von dem `CFUNCTYPE`-Aufruf stimmt auch nicht ganz, das erste Argument gibt nämlich den Typ des Rückgabewertes an. Der ist hier dann auch `ctypes.c_int`.
Mit den Modifikationen segfaultet das bei mir allerdings immer noch. Durch Ausprobieren hab ich dann rausgefunden, dass es wichtig ist, eine Referenz zu dem erzeugten CFUNCTYPE-Objekt für das Callback zu halten. Diese Lösung ist zwar ziemlich hässlich, segfaultet aber nicht und gibt eine Ausgabe. Inwieweit die "stimmt", weiß ich halt nicht ;)
Edit: Uh, mir fällt gerade auf, dass das mit der Referenz sogar in der Doku steht =D

Gruß,

Fred
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Das mit der Referenz zum Callback sollte hoffentlich klar sein, wenn man ein wenig darüber nachdenkt.

Auf gtk kann man verzichten und einfach eine Mainloop verwenden, die gobject zur Verfügung stellt (`g_main_loop_new()` und `g_main_loop_run()`).
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

fred.reichbier hat geschrieben:bei mir gibt es schon einen Segfault in gtk_init, das liegt daran, dass du die Funktion ohne Parameter aufrufst.
Tatsache, das habe ich übersehen. Interessant dass es dennoch funktioniert.
fred.reichbier hat geschrieben:Die Signatur von dem `CFUNCTYPE`-Aufruf stimmt auch nicht ganz, das erste Argument gibt nämlich den Typ des Rückgabewertes an. Der ist hier dann auch `ctypes.c_int`.
Uh, mein Fehler. Habe mir das ctypes-Beispiel nicht ausreichend angeschaut und nicht mitgedacht :oops: Wow, vielen Dank!
Trundle hat geschrieben:Auf gtk kann man verzichten und einfach eine Mainloop verwenden, die gobject zur Verfügung stellt (`g_main_loop_new()` und `g_main_loop_run()`).
Ja, das habe ich mir auch überlegt. Ist aber relativ egal, da libunique sowieso gegen GTK+ linkt und man um dieses Dependency nicht herumkommt. Aber generell plane ich schon auf die gobject-Schleife umzusteigen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Ok, ich habe jetzt mal snafus Tipp eingebaut, fred.reichbiers Code übernommen und versuche nun Trundles Tipp zu befolgen.

Sieht momentan so aus. Also GTK+ habe ich rausgenommen, aber wenn ich die Initialisierung von ``gtk_init()`` rausnehme beschwert sich gobject:

Code: Alles auswählen

(process:4123): GLib-GObject-CRITICAL **: /tmp/buildd/glib2.0-2.16.6/gobject/gtype.c:2248:
initialization assertion failed, use IA__g_type_init() prior to this function

(process:4123): GLib-CRITICAL **: g_once_init_leave: assertion `initialization_value !=
0' failed

(process:4123): GLib-GObject-CRITICAL **: g_object_new: assertion
`G_TYPE_IS_OBJECT (object_type)' failed
Daher dachte ich mir, ich setze da ein ``gobject.g_type_init()`` ein, wie gefordert. Das funktionier auch, aber gleich der nächste Funktionsaufruf steigt mit einem Segfault aus. Das sieht also nicht so optimal aus. Ich habe in ``gtk_init()`` reingeschaut, ebenso in ``gtk_init_check()``, aber dort habe ich auch nichts spezielles entdecken können, was GLib initialisieren würde.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten