C++ ObjectWrapper für Python

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.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,
booth hat geschrieben:Ich vestehe noch nicht wozu ich eigentlich ein Modul brauche. Kann ich Zeile 9 nicht einfach in der main()-Funktion aufrufen?
Welche main() ? Reden wir gerade über extending oder embedding? Wie auch immer: Die Antwort ist eher nein, es sei denn, Du machst seltsame Verrenkungen.
booth hat geschrieben:
Wahrscheinlich muss das Argument "_ImageProcessing_median" sein, oder? Gibt es bei dir dann noch eine Zeile ala:

Code: Alles auswählen

static PyMethodDef _ImageProcessingMethods[]
Und zu guter Letzt (vorerst). Wieso kann ich import_array() nicht einfach nach Py_Initialize(); aufrufen. Mein Compiler (mingw) meldet "statement with no value, in function returning 'int'". Was auch immer das heisst.

vG, U.
Jupp, es müsste etwas geben, das so aussieht:

Code: Alles auswählen

static PyMethodDef _ImageProcessingMethods[] {
  { name als string, Funktionsname , METH_VARARGS oder Vergleichbares, Docstring},
  {Senitnel} /* z. B. {NULL, NULL, NULL, NULL} */
}
Py_Initialize() riecht schon wieder so nach embedding - möchstest Du das machen? Ansonsten wirst Du eher PyMethodDef wollen.

HTH
Christian
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Nochmal in kurz was ich machen moechte (Code kommt unten).

1. Ich brauche eine dll (geht nicht anders). Zum testen tuts aber ein kleines Programm.exe. Die dll stellt eine Funktion zur Verfuegung, die als Parameter ein 2d Array hat. Damit ist das Ganze nur als Embedding zu machen, oder?

2. An die dll (oder fest in der main() definiert) uebergebe ich einen String, der an ein Python-Skript uebergeben wird zwecks regular expressions und numpy

3. Das Skript wertet den String aus und gibt mir ein numpy-Array zurueck.

4. Ich schreibe das numpy-Array in das C++-Array (Eingabeparameter der dll-Funktion).

Bei dem Code den ich bisher geschrieben habe halte ich mich ziemlich an die Vorlagen auf doc.python.org

Cheerio.

[Edit] Hier jetzt noch der Code, der nicht funktioniert:

Code: Alles auswählen

#include <iostream>
#include "Python.h"
#include "arrayobject.h"

int main()
{
	char *patternString = "pattern(10,100,8,1,2,3,4,5,6,7,8)";
	PyObject *pName, *pModule, *pFunc;
	PyObject *pArgs, *pValueTuple;

	Py_Initialize();
	import_array();

	/* Error checking of pName left out */
	pName = PyString_FromString("pattern"); // Name of the python file (pattern.py)
	pModule = PyImport_Import(pName); 
	Py_DECREF(pName);

	if (pModule != NULL) {
		pFunc = PyObject_GetAttrString(pModule, "numpyA"); // Call function from module
		/* pFunc is a new reference */

		pArgs = PyTuple_New(1); // Arguments to call function with. 
		PyTuple_SetItem(pArgs, 0, PyString_FromString(patternString)); // Fill argument tuple

		if (pFunc && PyCallable_Check(pFunc)) {
			pValueTuple = PyObject_CallObject(pFunc, pArgs);
			Py_DECREF(pArgs);

			PyArrayObject *a;
			PyArg_Parse(pValueTuple, "O!", &PyArray_Type, &a);
			//std::cout << PyTuple_Check(testTuple1) << std::endl;
			Py_XDECREF(a);
			Py_XDECREF(pValueTuple);
		}
		else { // Either pFunc is not a function or not callable. A problem anyway
			if (PyErr_Occurred())
				PyErr_Print();
			fprintf(stderr, "Cannot find function \"%s\"\n", "getValues");
		}
		Py_XDECREF(pFunc); // XDECREF decrement the count for pFunc. Same as DECREF, but argument is allowed to be NULL
		Py_DECREF(pModule);
	}
	else {
		PyErr_Print();
		fprintf(stderr, "Failed to load \"%s\"\n", "pattern.py");
		return 1;
	}
	Py_Finalize();
	return 0;
}
Und das Python-Skript

Code: Alles auswählen

from numpy import *

def numpyA(inSdump):
    a = array([1,2,3,4,5],[5,4,3,2,1])
    return a
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

booth hat geschrieben:1. Ich brauche eine dll (geht nicht anders). Zum testen tuts aber ein kleines Programm.exe. Die dll stellt eine Funktion zur Verfuegung, die als Parameter ein 2d Array hat. Damit ist das Ganze nur als Embedding zu machen, oder?
Nein, das ist in sich widersprüchlich: Wenn Du eine dll (oder ein shared object (.so) unter Linux) willst, die eine Funktion zur Verfügung stellt, die Du in Python importieren kannst, dann ist das nicht embedding, sondern extending - und zwar von Python. Mit embedding bezeichnet man das "Einbauen" von Python in ein anderes (C/C++)-Programm. Ergo brauchst Du auch kein Executable, sondern nur die dll, deren Funktion Du importierst.
booth hat geschrieben:2. An die dll (oder fest in der main() definiert) uebergebe ich einen String, der an ein Python-Skript uebergeben wird zwecks regular expressions und numpy
Wieso das? Wieso ein String, wenn es ein numpy array sein soll?
booth hat geschrieben:3. Das Skript wertet den String aus und gibt mir ein numpy-Array zurueck.
Wie auch immer, wenn Du einen String an eine Funktion in C++ übergeben willst und ein numpy-Array zurückkommen soll, dann mußt Du letzters erst erzeugen. Das ginge z. B. mit PyArray_NewFromDescr oder PyArray_New und Konsorten. Aber Du gibst ja gar nichts zurück - außer einem Integer.
booth hat geschrieben:4. Ich schreibe das numpy-Array in das C++-Array (Eingabeparameter der dll-Funktion).
Also da blicke ich nicht so ganz durch:
Du deklarierst patternString und steckst es in eine Funktion, die einen Parameter akzeptiert, aber diesen nicht, sondern - wenn man von dem Fehler (s.u.) absieht - ein anderes array zurückgibt. (Tipp: Mit "inSdump" passiert gar nichts.)

In dem Python-Skript erzeugst Du kein Array, es sei denn Du hast numpy.ndarray umgeschrieben ;-). Bei mir erzeugt

Code: Alles auswählen

array([1,2,3,4,5],[5,4,3,2,1])
einen Traceback:

Code: Alles auswählen

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: data type not understood
Warum zeigt Dir

Code: Alles auswählen

>>> help(array)
Wenn Du tatsächlich ein 2d-array erzeugen willst, dann z. B. so:

Code: Alles auswählen

array([[1,2,3,4,5],[5,4,3,2,1]])
So, jetzt kannst Du entweder die Pythonfunktion entsprechend ändern, damit Du tatsächlich mit den Werten so arbeitest, wie Du es wünscht. Oder, falls Dein Wunsch ist von Python aus auf kompilierte Funktionen zurückgreifen zu können, solltest Du im C-Code tatsächlich auch Funktionen deklarieren (und definieren sowieso ;-) ). Wie, hat HerrHagen gezeigt.

Gruß,
Christian

PS Vielleicht solltest Du demnächst nicht alte Threads wiederbeleben, um dort Fragen zu stellen, die nicht zum Kontext passen. Das verwirrt und hilft Dir erst einmal nicht weiter und anderen im Forum, diie suchen, auch nicht.
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Hmm. Scheinbar schaffe ich es nicht mich klar auszudruecken. Sorry.

Ich moechte keine C++-Funktion in Python benutzen. Also nicht extending, sondern Python-Funktionen in C++ einbinden (verstehe ich unter embedding). Also, ich moechte aus C++ eine Pythonfunktion aufrufen (mit zB einem String als Parameter) und etwas aus Python zurueckbekommen (naemlich das array).

Wie genau aus dem string ein Array wird habe ich rausgelassen weil nicht interesant und auch nicht auf den Syntax geachtet (sorry). Wie gesagt, der Code funktioniert nicht, er sollte nur illustrieren, was ich machen will. Das der input-String nicht benutzt wird ist zur Zeit so gewollt, ist ja nur ein Testprogramm, dass dann erweitert wird, wenn die erste Zeile mal tut :)

Zum Thema altes Thread wiederbeleben. Ich scheue mich immer davor neue Threads aufzumachen, da meistens die ersten beiden Seiten nur Kommentare im Stil von "Benutz die Suche" enthalten. Ich fand meint Thema passte gut in diesen Thread. Werde ich in Zukunft dann anders machen.
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

O.K. Hab's hingekriegt. Der Vollstaendigkeit halber (vielleicht hilft's ja mal wem) hier der Code, auch wenn der nichts Sinnvolles macht. Ausserdem enthaelt er kaum Fehlerroutinen. :roll:

Kurzbeschreibung: Der Code ruft ein Python-Skript (pattern.py) auf. Selbiges gibt ein numpy-Array zurueck, dass dann auf die Console ausgegeben wird.

Edit Programm auf Grund der Empfehlungen in den Folgekommentaren abgeaendert.

Code: Alles auswählen

#include <iostream>
#include "Python.h"
#include "arrayobject.h"

int main()
{
	PyObject *pName, *pModule, *pFunc;
	PyObject *pValue;

	Py_Initialize();
	_import_array();

	pName = PyString_FromString("pattern"); // Name of the python file (pattern.py)
	pModule = PyImport_Import(pName); 
	Py_DECREF(pName);

	if (pModule != NULL) {
		pFunc = PyObject_GetAttrString(pModule, "numpyA"); // Call function from module
		
        if (pFunc && PyCallable_Check(pFunc)) { // If pFunc exists and is callable, than it's a function and we can proceed
				pValue = PyObject_CallObject(pFunc, NULL); // Call function with no arguments, return value to a python variable

				// Schreiben des Arrays
				if (PyArray_Check(pValue)){
					for (int ic=0; ic<PyArray_DIM(pValue,0); ic++){
						for (int jc=0; jc<PyArray_DIM(pValue,1); jc++){
							std::cout << *(double*) PyArray_GETPTR2(pValue, ic, jc) << std::endl;
						}
					}
					Py_XDECREF(pFunc); // XDECREF decrement the count for pFunc. Same as DECREF, but argument is allowed to be NULL
					Py_DECREF(pModule);
				}
				else {
					Py_XDECREF(pFunc);
					Py_DECREF(pModule);
					if (PyErr_Occurred())
						PyErr_Print();
					fprintf(stderr, "Function \"%s\" didn't return a numpy array!\n", "getValues");
					return 1;
				}
			}
				else {
					Py_XDECREF(pFunc);
					Py_DECREF(pModule);
					if (PyErr_Occurred())
						PyErr_Print();
					fprintf(stderr, "Function \"%s\" didn't return a numpy array!\n", "getValues");
					return 1;
				}
			}
			else { // Either pFunc is not a function or not callable. A problem anyway
				Py_XDECREF(pFunc);
				Py_DECREF(pModule);
				if (PyErr_Occurred())
					PyErr_Print();
				fprintf(stderr, "Cannot find function \"%s\"\n", "getValues");
				return 1;
			}
		Py_XDECREF(pFunc);
		Py_DECREF(pModule);
	}
	else {
		PyErr_Print();
		fprintf(stderr, "Failed to load \"%s\"\n", "pattern.py");
		return 1;
	}
	Py_Finalize();
	return 0;
}
Und dann noch der ebenfalls etwas sinnlose Python-Code fuer pattern.py:

Code: Alles auswählen

from numpy import *

# Function definition
def numpyA(inSdump): # Inputstring noch unnuetz.
    a = array([[1.1,2.1,3.1,4.1,5.1],[5.2,4.2,3.2,2.2,1.2]])
    return a
Vielen Dank fuer eure Hilfen!
Zuletzt geändert von booth am Freitag 13. Februar 2009, 10:40, insgesamt 3-mal geändert.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

So wirklich hübsch ist das aber alles nicht. `PyArg_Parse()` zu benutzen, um den Typ zu überprüfen, ist ja einfach nur Missbrauch. Dafür gibt es `PyObject_TypeCheck()`, außerdem bringt da numpy `PyArray_Check()`mit. Davon abgesehen geben die ganzen PyArg*-Funktionen geliehene Referenzen bei Python-Objekten zurück, die darf man also nicht `Py_DECREF`en.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

`PyArg_Parse()` zu benutzen, um den Typ zu überprüfen, ist ja einfach nur Missbrauch.
Wieso? Was spricht dagegen? Ist doch extra für diesen Zweck vorgesehen.
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Trundle hat geschrieben:So wirklich hübsch ist das aber alles nicht. `PyArg_Parse()` zu benutzen, um den Typ zu überprüfen, ist ja einfach nur Missbrauch. Dafür gibt es `PyObject_TypeCheck()`, außerdem bringt da numpy `PyArray_Check()`mit. Davon abgesehen geben die ganzen PyArg*-Funktionen geliehene Referenzen bei Python-Objekten zurück, die darf man also nicht `Py_DECREF`en.
Mag sein, dass das nicht ideal ist, aber es tut erstmal. Ich benutze 'PyArg_Parse()' nicht um den Typ zu ueberpruefen, oder? Vielleicht brauche ich es gar nicht, weil pValue ja schon ein numpy-Array ist. Eigentlich ein guter Punkt...

Die Sache mit dem Py_DECREF und Py_XDECREF, geliehenen Referenzen etc. Habe ich noch nicht durchschaut. Wie gesagt, ich habe mich da moeglichst an die Vorlagen von doc.python.org gehalten. Da stand, wenn man sich nicht sicher ist soll man Py_XDECREF nehmen und ich glaube das habe ich ueberall genommen (anscheind nicht ;) ). Mir ist die Funktion halt noch nicht so klar.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

In der Dokumentation steht: "It does remain a convenient way to decompose other tuples, however, and may continue to be used for that purpose." Es wird hier aber kein Tupel entpackt, sondern überprüft, ob ein Python-Objekt einen bestimmten Typ hat, und dafür ist es IMHO die falsche Funktion.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Ueberprueft wird da eigentlich nichts, weil mit dem Rueckgabewert gar nichts gemacht wird. Ich habe das nur kopiert und nachtraeglich nicht angepasst. Urspruenglich wurde in Zeile 25 ein beliebiges PyObject zurueck gegeben und mit Zeile 29 wurde das PyObject dann in C++-Variablen geschrieben/zerlegt.

Wahrscheinlich brauche ich die Zeile 29 nicht mehr, weil ich ja weiss, dass ich nur ein numpy-Array zurueckgebe. Sogesehen koennte ich auch nur darauf testen und irgendwelche Fehlerbehandlungen (die es bisher ja noch gar nicht gibt) einbauen.
booth
User
Beiträge: 44
Registriert: Mittwoch 31. Oktober 2007, 21:30
Wohnort: Durham, U.K.

Ich habe das Programm mal abgeaendert. Die Zeile mit PyArg_Parse brauchte man tatsaechlich nicht und Py_XDECREF war auch falsch (ich weiss aber nicht warum :) ). Egal, nu tut's.

Nochmal vielen Dank fuer die Hilfe.
Antworten