Verwendung von Py_DECREF

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Hallo :),
ich habe eine C-Extension in welche wiederum ein Python-Modul eingebettet ist.
Beim wiederholten Zugriff auf die C-Extension im Verlauf des Scripts traten Abstürze auf.
Ich habe festgestellt, daß diese anscheinend auf die falsche Verwendung der "Py_DECREF"-Funktion zurückzuführen sind.
Wie wird diese Funktion richtig verwendet?
Muss/Darf die Funktion auf alle als "PyObject" deklarierten Variablen angewendet werden, bevor die C-Funktion beendet wird?
Ist die Reihenfolge wichtig?

Vielen Dank vorab
BlackJack

@hypnoticum: Sie darf nicht einfach auf alle angewendet werden, sondern nur auf solche, bei denen die Funktion auch "der Eigentümer" ist. Die Frage solltest Du gewissenhaft prüfen. Insbesondere musst Du auch darauf achten was andere Funktionen, die Du mit dem Objekt als Argument aufgerufen hast, mit dem Referenzzähler machen. Stichwort "Referenzen stehlen".
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

@Blackjack: Vielen Dank für die schnelle Antwort, das hilft mir schonmal etwas weiter.
Bisher hatte ich es so realisiert (also mit Fehler):

Code: Alles auswählen

// To display errors an embedded Python Script will be interpreted
PyObject *PyMsgBoxModule = NULL, *PopUpMsgClass = NULL, *PopUpMsgInstance = NULL, *PyMsgBoxDict = NULL, *PyMsgBoxRet = NULL;
PyMsgBoxModule = PyImport_ImportModule("PyMsgBox");
if(PyMsgBoxModule){
	PyMsgBoxDict = PyModule_GetDict(PyMsgBoxModule);
	PopUpMsgClass = PyDict_GetItemString(PyMsgBoxDict, "PopUpMsg");
	if (PyCallable_Check(PopUpMsgClass)){
		PopUpMsgInstance = PyObject_CallObject(PopUpMsgClass, NULL);
	}
	else
		printf("Error: Python PopUpMsg class in module PyMsgBox not found\n");
}
else
	printf("Error: PyMsgBox module not found\n");

.
.
.

Py_DECREF(PyMsgBoxModule);
Py_DECREF(PopUpMsgClass);
Py_DECREF(PopUpMsgInstance);
Py_DECREF(PyMsgBoxDict);
if (PyMsgBoxRet != NULL){
	Py_DECREF(PyMsgBoxRet);
}
das Python Modul ist:

Code: Alles auswählen

import win32gui

class PopUpMsg(object):
    def __init__(self):
        self._msg   = "Box-Message"
        self._title  = "Box-Title"
        self._btn = 0
        
    def dispError(self, File, Line, l_vCmuErrMsg, Title, Button):
        self._msg   = 'Error in file ' + File + ', Line ' + str(Line) + ': ' + l_vCmuErrMsg
        self._title  = Title
        self._btn = Button
        return win32gui.MessageBox(0, self._msg, self._title, self._btn)
eigentlich hätte ich angenommen, dass die mit "Py_DECREF" verwendeten "PyObject"-Zeiger alle der C-Funktion "eigen" sind
BlackJack

@hypnoticum: Wenn ich mich nicht verlesen habe in der Doku, sollte das der Fall sein. Einen Zwischenschritt kannst Du Dir übrigens sparen: Statt das `dict` des Moduls zu holen und dort dann die Klasse heraus zu holen, kannst Du auch gleich das Attribut vom Modul holen:

Code: Alles auswählen

PopUpMsgClass = PyObject_GetAttrString(PyMsgBoxModule, "PopUpMsg");
Und `Py_XDECREF()` könnte vielleicht interessant sein.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Äh, dir ist schon bewusst, dass du da mit Nullpointern rumhantierst? Die ganzen DECREFS da. Das Übergebene kann jeweils NULL sein, sodass also Py_DECREF(NULL) gemacht wird. Das crasht natürlich :-)

Außerdem solltest du du die Python-Fehlerbehandlung verwenden und nicht mit printf rumhantieren.
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

@Dauerbaustelle:
Nein, ist mir nicht bewusst und auch nicht einsichtig. Wie erkenne ich, wann ein Pointer ins "Leere" zeigt ? Ich dachte die Pointer würden nur mit "NULL" initialisiert. Im Verlauf des Programms erfolgen dann ja Zuweisungen von denen ich dachte sie wieder freigeben zu müssen.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

hypnoticum: Es gibt aber laut dem obigen Code-Beispiel Fälle, in denen das NULL nie geändert wird, zum Beispiel wenn `PyMsgBoxModule` NULL ist (und somit der Teil innerhalb der geschweiften Klammern nicht evaluiert wird).
hypnoticum
User
Beiträge: 132
Registriert: Dienstag 15. März 2011, 15:43

Gibt es eine Möglichkeit zu sehen ob/welche Referenzen nach dem Aufruf einer Extension noch im Speicher gehalten werden?
Ich meine wie überprüfe ich, ob ich richtig aufgeräumt habe?
Gibt's ne Möglichkeit während der Laufzeit einer Extension zB mit printf() die Referenzzähler zu beobachten?
Und wann kann/sollte ich Py_DECREF(Ref) nutzen, wenn der return Wert der Extension eben diese Referenz 'Ref' ist? Wird die Referenz dann 'gestohlen' und muss von der aufrufenden Funktion behandelt werden?
BlackJack

@hypnoticum: Du könntest Python als "debug build" übersetzen. Dann gibt es im `gc`-Modul noch ein paar nützliche Funktionen.

`Py_DECREF` benutzt man immer auf Referenzen die man "besitzt" und wo man diesen Besitz aufgeben möchte. Bei Rückgabewerten möchte man das nicht, denn da geht der "Besitz" auf den Aufrufer über.
Antworten