[C] Klassenfunktion aufrufen

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Sonntag 16. Mai 2010, 02:12

Hallo.
Wie realisiere ich es, dass ich auch Klassenfunktionen (Memberfunktionen, blabla) aufrufen kann? Also sowas:

Code: Alles auswählen

class X(object):
    def __init__(self):
        cmodule.register(self.func)
    
    def func(self, blah):
        print blah

def func_noclass(blah):
    print blah

cmodule.register(func_noclass)
Der Aufruf zu func_noclass funktioniert problem, X.func crasht aber.
Hier mal der Codeausschnitt: http://www.python-forum.de/pastebin.php?mode=view&s=12

Gibt es eigentlich sowas ähnliches wie PyEval_Check oder so? Ich konnte nichts dergleichen finden.

Edit: Gibt es eigentlich eine Möglichkeit, ein C-Modul wieder zu entladen?

Gruß
Grüßle.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Sonntag 16. Mai 2010, 13:33

Ich glaube, das ist ein Denkfehler deinerseits. `X.func` operiert doch immer AUF einem Objekt von `X` (welches per Konvention an den Namen `self` gebunden wird) -- wenn du jetzt also `X.func(42)` aufrufst, gibt es ja gar kein Objekt, auf dem die Methode operieren kann!

Vielleicht suchst du aber auch nur einfach sowas wie @staticmethod oder @classmethod.

Edit: `PyEval_Check`? Was soll das tun?
Edit2: "Entladen": Ich glaube, das geht, indem du alle Referenzen auf das Modulobjekt entfernst (also schlussendlich auch aus `sys.modules` entfernst). Aber warum willst du das?
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Sonntag 16. Mai 2010, 13:41

Ich weiß nicht, wieviele sinnvolle Antworten du dir daurauf erhoffst, aber zu "X.func crasht" kann man ohne den Quelltext zu sehen einfach gar nichts sagen (ohne zu raten). Bei C-Extensions ist beispielsweise die Referenzzählerei sehr wichtig, und davon sieht man exakt gar nichts.

Und der Fetzen Code, den du da zeigst, ist auch noch falsch: Der Rückgabewert von `Py_BuildValue` wird nicht überprüft und außerdem müsstest du explizit ein Tupel erstellen, wenn du das `PyEval_CallObject` übergibst (also "(s)" anstatt "s").
Chrisber hat geschrieben:Edit: Gibt es eigentlich eine Möglichkeit, ein C-Modul wieder zu entladen?
Nein.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Sonntag 16. Mai 2010, 14:36

Am schönsten, weil expliziter, finde ich da

Code: Alles auswählen

PyObject* args = PyTuple_Pack(/* size */ 1, ...);
if(args == NULL)
  stirb();
PyObject* rv = PyObject_CallObject(..., args);
/* Nicht vergessen: */
Py_DECREF(args);
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Sonntag 16. Mai 2010, 14:40

Hi.
@ Dauerbaustelle: Das ich nicht X.func registriere ist mir auch klar. Ich wollte damit nur zeigen, dass ich die Funktion von der Klasse X meine.
Das muss auch ohne staticmethod gehen...
PyEval_Check - damit meine ich ein Äquivalent zu ``callable``. Ich habe jetzt das hier gefunden:
http://docs.python.org/c-api/object.htm ... able_Check
Problem ist jedoch, dass mich der Satz dort verwirrt: "This function always succeeds." - heißt das, dass es immer 1 returnt?

@ Trundle: Ich bekomme die Callbackfunktion über PyArg_ParseTuple - muss ich da einen Reference Count ausführen?
Das mit dem Py_BuildValue wusste ich nicht, danke!

Gruß
Grüßle.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Sonntag 16. Mai 2010, 15:17

Chrisber hat geschrieben:@ Dauerbaustelle: Das ich nicht X.func registriere ist mir auch klar. Ich wollte damit nur zeigen, dass ich die Funktion von der Klasse X meine.
Das muss auch ohne staticmethod gehen...
Dafür ist static-/classmethod nunmal da. Wenn du das nicht verwenden willst musst du die Instanz/Klasse halt selbst übergeben.
Problem ist jedoch, dass mich der Satz dort verwirrt: "This function always succeeds." - heißt das, dass es immer 1 returnt?
Nein, das heißt, dass die Funktion immer funktioniert. Also du immer eine 0 oder 1 zurück kriegst und nichts anderes.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Sonntag 16. Mai 2010, 15:19

Chrisber hat geschrieben:Hi.
@ Dauerbaustelle: Das ich nicht X.func registriere ist mir auch klar. Ich wollte damit nur zeigen, dass ich die Funktion von der Klasse X meine.
Das muss auch ohne staticmethod gehen...
Dann zeig doch mal, was genau der Fehler ist, den du bekommst.
Problem ist jedoch, dass mich der Satz dort verwirrt: "This function always succeeds." - heißt das, dass es immer 1 returnt?
Return 1 if the object is callable and 0 otherwise
Ja, genau, es kommt immer 1 zurück.
Ich bekomme die Callbackfunktion über PyArg_ParseTuple - muss ich da einen Reference Count ausführen?
Du musst den Reference-Count erhöhen, wenn du `PyArg_ParseTuple` verwendest, ja. Trundle meinte aber was anderes.
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Sonntag 16. Mai 2010, 15:33

Hi.
Danke erstmal.

@ Dauerbaustelle: Fehler ist der sofortige Crash des Programms... Was soll ich dir da denn liefern?
Wenn ich den Reference Count erhöht habe, dann nach dem beenden der Funktion wieder einen "De"-Reference Count ausführen, richtig?

Gruß
Grüßle.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Sonntag 16. Mai 2010, 16:13

Ein kleiner auf das Minimum reduzierter Testfall, der das Problem aufzeigt, wäre ganz, denn dann sieht man was du machst und muss nicht wild im Dunkeln rumstochern.

Prinzipiell erniedrigst du den Referenzzähler eben wieder, wenn du den Wert "wegschmeißt" - also beispielsweise, wenn deine `register`-Funktion einen neuen Callback speichert.

Oder du benutzt einfach Cython und machst dir um all das überhaupt keine Gedanken mehr.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
BlackJack

Sonntag 16. Mai 2010, 16:47

@Trundle: Refrenzzähler sind hart im Nehmen, die kann man nicht so leicht erniedrigen. Verringern kann man sie aber. ;-)
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Sonntag 16. Mai 2010, 19:25

Chrisber, gdb-Traceback zum Beispiel...
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Sonntag 23. Mai 2010, 15:01

Hi.
Ich habe es nun hingekriegt. Problem war, dass ich (durch einige Umstände im Code) die Klasse immer vorm Aufrufen gelöscht habe -.- (ja, ich schäme mich!)

Dennoch habe ich jetzt ein weiteres Problem: Wenn ich einen Callback einer statischen Funktion speichere (also den PyObject-Pointer), funktioniert das wunderbar (ich kann ihn später problemlos wiederfinden). Jedoch funktioniert das ganze nicht, wenn ich einen Callback auf eine Memberfunktion speichern will. Beispiel:

Code: Alles auswählen

myplugin.bla.hook("xblub", xblub) # Adresse von xblub z.B. 0x10a7bbf0
myplugin.bla.hook("instanceblub", instance.blub) # Adresse von instance.blub HIER z.B. 0x1029b1e8
# direkt danach
myplugin.bla.unhook("xblub", xblub) # Adresse von xblub ist immernoch 0x10a7bbf0 - kann ich als problemlos wiederfinden
myplugin.bla.unhook("instanceblub", instance.blub) # Adresse von instance.blub hat sich geändert: 0x1029b210
Problem ist nun natürlich, dass ich das nicht mehr wiederfinden kann, dementsprechend den Hook nicht entfernen kann - und das resultiert später in einem Fehler.

Hat da jemand eine Idee, wie ich das Problem lösen könnte? Gibt es da vielleicht so etwas wie PyInstance_IsInstance(<adresse vom hook>, <adresse vom unhook>) oder so?

Edit: In der API steht bei PyList_Append leider nichts davon, ob es einen neuen Reference Count erstellt oder nicht. Logisch wäre es natürlich. Was meint ihr dazu?

Gruß
Grüßle.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Sonntag 23. Mai 2010, 16:00

Eigentlich darf sich die Adresse nicht ändern. Warum sollte sie? Ich denke, das Problem sitzt wo anders :-)
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Sonntag 23. Mai 2010, 16:08

Naja. Was sollte hier denn falsch sein?

Code: Alles auswählen

import es, nativetools as nt

def load():
	nt.concommand.hook("maps", maps_hook)
	nt.concommand.hook("maps", maps_hook2)
	nt.concommand.hook("maps", instance.maps_hook3) // hier die Klasseninstanz
	nt.concommand.hook("status", status_hook)
	nt.server.registerMapEndCallback(map_end)

def unload():
	nt.concommand.unhook("maps", maps_hook)
	nt.concommand.unhook("maps", maps_hook2)
	nt.concommand.unhook("maps", instance.maps_hook3) // hier wird es dann nicht mehr gefunden da die Adresse anders ist
	nt.concommand.unhook("status", status_hook)
	nt.server.unregisterMapEndCallback(map_end)

def maps_hook(args):
	es.msg("Hook 1")
	return False

def maps_hook2(args):
	es.msg("Hook 2")
	return False

class mapsHook(object):
	def maps_hook3(self, args):
		es.msg("Hook 3")
		es.fail()
		return False

instance = mapsHook()

def status_hook(args):
	es.msg("Status Command executed!")
	es.msg("UserID: %i" % nt.concommand.getUserID())
	return False

def map_end():
	es.msg("Map ends now!")
Das ist das ganze Script (ungekürzt um Fehler nicht wegzuschneiden). Mein Plugin ist nativetools.

Gruß
Grüßle.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Sonntag 23. Mai 2010, 16:24

Ich meine im C-Programm.
Antworten