Embedded Python in c++ + Probleme mit dynamischen typen

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
NoRulez
User
Beiträge: 36
Registriert: Donnerstag 1. November 2007, 15:31

Hey @all,

da das mein erster Eintrag ist, hoffe ich das ich in diesem Forum hier richtig bin, ansonsten sorry und bitte verschieben.

Also mir geht es darum das ich in c++ beispielsweise ein funktion habe wie folgt:

Code: Alles auswählen

void* func(std::string _module, char* _arg ...);
der ich beliebig viele parameter übergeben kann.

jetzt habe ich sagen wir 5 python scripts.
Ich habe mich auch schon mit den funktionen PyInt_AsString udgl. herumgespielt, leider kein erfolg.

Mein Problem ist nun, das ich ja nie vorher weiß was ich im python script zurückliefere, beispielsweise wenn ein anderer entwickler ein python script hinzufügt, kann ich ja nicht immer davon ausgehen das es ein String udgl. ist.

Kann man irgendwie überprüfen welcher Typ das PyObject* hat? und dann die entsprechende konvertierungsfunktion aufrufen? Wenn JA, wie?

Weiters habe ich ein Problem mit der übergabe. Ich kann beispielsweise einen Int übergeben und das so oft wie nötig. Was ist aber wenn ich verschiedene Typen habe (int, float, string) wie übergebe ich die Dynamisch?

Hoffe es versteht jemand was ich meine und kann mir helfen bzw. tips geben

Lg NoRulez
sagen wir ich habe in einem
Zuletzt geändert von NoRulez am Samstag 3. November 2007, 13:37, insgesamt 1-mal geändert.
BlackJack

Also mir ist nicht klar was Du machen möchtest. Was genau hast Du? Was willst Du haben?

Die Funktion da, soll die von Python aus aufgerufen werden? Wie verbindest Du C++ und Python überhaupt?

Ein beliebiges Objekt in eine Zeichenketten-Representation überführen geht mit der `str()`-Funktion.
NoRulez
User
Beiträge: 36
Registriert: Donnerstag 1. November 2007, 15:31

Also hier mal der C++Source um das Python-Modul und die funktion zu laden:

Code: Alles auswählen

void func(std::string _module, char* _arg ...) {
	std::vector<std::string> py_args;
	std::vector<std::string> arguments;
	std::string bak_dir = "";

	va_list ap; // will point to each unnamed argument in turn
	int num;
	double d;
	std::string s;
	char* ss;
	const char *p=_arg;
	va_start(ap, _arg); // point to first element after _arg
	while(*p) {
		switch(*p) {
			case 'i': // int
				num=va_arg(ap,int);
				py_args.push_back(NodeConfigFunctions::LongToStr(num));
				//printf("%d",num);
				break;
			case 'd': //double
				d=va_arg(ap,double);
				py_args.push_back(NodeConfigFunctions::LongToStr(d));
				//printf("%f",d);
				break;
			case 's': // char*
				ss=va_arg(ap,char*);
				s = ss;
				py_args.push_back(s);
				//printf("%s",s.c_str());
				break;
			case 'S': // std::string
				s=va_arg(ap,std::string);
				py_args.push_back(s);
				//printf("%s",s.c_str());
				break;
			default:
				printf("unsupported format flag");
		}
		++p;
	}
	va_end(ap); //cleanup

	try {
		if(py_args.empty())
			throw FileError("Cannot find any functions");

		std::string py_module_name = _module;
		std::string py_function = py_args[0];
		py_args.erase(py_args.begin());

		PyObject *pName, *pModule, *pDict, *pFunc;
		PyObject *pArgs, *pValue, *pValueRepr;

		pName = PyString_FromString(py_module_name.c_str());
		/* Error checking of pName left out */

		// Change the module directory and remember the current directory
		if(!this->directory.empty()) {
			bak_dir = _getcwd(NULL, 0);
			_chdir(this->directory.c_str());
		}
	
		// Import python module
		pModule = PyImport_Import(pName);

		// Change to the previous directory
		if(!this->directory.empty())
			_chdir(bak_dir.c_str());

		if (pModule != NULL) {					        
			// pFunc is a new reference
			pFunc = PyObject_GetAttrString(pModule, py_function.c_str());

			if (pFunc && PyCallable_Check(pFunc)) {
				pArgs = PyTuple_New(py_args.size());
				for (int i = 0; i < py_args.size(); ++i) {
					//pValue = PyInt_FromLong(atoi(py_arguments[i].c_str()));
					pValue = PyString_FromString(py_args[i].c_str());
					if (!pValue) {
						//Py_DECREF(pArgs);
						//Py_DECREF(pModule);
					   /* fprintf(stderr, "Cannot convert argument\n");
						return 1;*/
						char buffer[2048] = {0};
						sprintf(buffer, "Cannot convert argument\n");
						throw FileError(buffer);
					}
					/* pValue reference stolen here: */
					PyTuple_SetItem(pArgs, i, pValue);
				}
				pValue = PyObject_CallObject(pFunc, pArgs);
				pValueRepr = PyObject_Repr(pValue);
				//Py_DECREF(pArgs);
				if (pValue != NULL) {
					std::string value = PyString_AsString(pValueRepr);
					trim(value, '\"');
					printf("%s\n", value.c_str());
					//Py_DECREF(pValue);
				}
				else {
					//Py_DECREF(pFunc);
					//Py_DECREF(pModule);
					PyErr_Print();
					/*fprintf(stderr,"Call failed\n");
					_sleep(5000);
					return 1;*/
					char buffer[2048] = {0};
					sprintf(buffer, "Call failed\n");
					throw FileError(buffer);
				}
			}
			else {
				if (PyErr_Occurred()) {
					PyErr_Print();
					//_sleep(5000);
				}
				/*
				fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
				_sleep(5000);*/
				char buffer[2048] = {0};
				sprintf(buffer, "Cannot find function \"%s\"\n", py_function.c_str());
				throw FileError(buffer);
			}
		}
		else {
			PyErr_Print();
			char buffer[2048] = {0};
			sprintf(buffer, "Failed to load \"%s\"\n", py_module_name.c_str());
			throw FileError(buffer);
			/*fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
			_sleep(5000);
			return 1;*/
		}

		Py_Finalize();
	}
	catch(FileError &e) {
		cerr << e.what() << endl;
	}

}

Was möchte ich machen?
ich will von C++ aus Python scripts ausführen und deren return values in C++ weiterverarbeiten. Mit Statischen typen funktioniert und ist mehr oder weniger ein kinderspiel. Mir geht es um dynamisches Datentypen die mir vorher nicht bekannt sind.

Beispiel was funktioniert (sehr "simple" beispiel):
TypA (von C++) = string oder int, ...
TypB (VonPython) = string oder int, ...

1.) C++ (TypA) => übergabe an Python(TypB)
2.) Result von Python(TypB) => übergabe an C++ (TypA)
3.) weiterverwendung von TypA...

aufruf unter c++:

Code: Alles auswählen

func("testmodul", "myFunc", "Teststring");
testmodul.py:

Code: Alles auswählen

def myFunc(arg):
	return arg;
So in diesem Beispiel wären mir also unter C++ folgende Funktionen bekannt:
C++ => Python ... PyString_ToString
Python => C++ ... PyString_AsString
Für int, float usw. gibts equivalente Funktionen

Jetzt hat aber ein Programmierer ein Python script hinzugefügt, welches einen Int zurückliefert.
Nun habe ich dann ein Problem, weil PyString_AsString nicht funktioniert, stattdessen müsste ich die Funktion PyInt_AsString verwenden.
Nur vorher weiß ich was für einen Typ ich zurückbekomme?
bzw. wie weiß ich was für einen Typ ich übergebe?

Beispiel:

Code: Alles auswählen

def myFunc(arg):
    if(arg == "test1"):
        return 1
    elif(arg == "test2"):
        return 2
    else:
        return 0
Hier müsste ich PyString_ToString und von Python nach C++ zurück PyInt_AsInt verwenden, nur woher weiß ich das.
Kann man irgendwie ermitteln welchen Typ das PyObject hat und dieses dann dementsprechend casten?
Und/Oder kann man ermitteln welche typen eine funktion benötigt?

Lg NoRulez[/code]
Zuletzt geändert von NoRulez am Samstag 3. November 2007, 13:40, insgesamt 4-mal geändert.
NoRulez
User
Beiträge: 36
Registriert: Donnerstag 1. November 2007, 15:31

Ich habe gerade die Funktion "Py_BuildValue", könnte das die unktion sein die ich vergeblich suche? Kennt sich jemand mit dieser Fuktion aus?

Lg NoRulez
NoRulez
User
Beiträge: 36
Registriert: Donnerstag 1. November 2007, 15:31

Ich habe es im moment für den Return-Wert so gelöst:

Code: Alles auswählen

            pValue = PyObject_CallObject(pFunc, pArgs);
			//Getting return Value:
			Py_DECREF(pArgs);
            if (pValue != NULL) {

					if(type == "int") {
						printf("as INT: %d\n", PyInt_AsLong(pValue));
					}
					else if(type == "long") {
						bool o = false;
						__int64 val = 0;
						o = PyLong_AsI64(pValue, &val);

						bool ok = false;
						unsigned __int64 val2 = 0;
						ok = PyLong_AsUI64(pValue, &val2);

            if(ok)
						  printf("value using UNSIGNED INT 64: I64u: %I64u\n", val2);
					}
					else if(type == "str") {
						printf("as STRING: %s\n", PyString_AsString(pValue));
					}
					else if(type == "float") {
						printf("as UNICODE: %0.f\n", PyFloat_AsDouble(pValue));
					}
					else if(type == "unicode") {
						printf("as UNICODE: %s\n", PyUnicode_AsUnicode(pValue));
					}
					else {
						cout << "Type: " << type << endl;
						cout << "        Value: " << pValue << endl;
					}

                Py_DECREF(pValue);
            }
            else {
                Py_DECREF(pFunc);
                Py_DECREF(pModule);
                PyErr_Print();
                /*fprintf(stderr,"Call failed\n");
				_sleep(5000);
                return 1;*/
				char buffer[2048] = {0};
				sprintf(buffer, "Call failed\n");
				throw FileError(buffer);
            }
Aber ich denke das es garantiert noch eine elegantere Lösung gibt. Bin für jeden Vorschlag Dankbar.

Vorschläge um die Bindung C++/Python zu vereinfachen wäre auch toll.

Lg NoRulez
BlackJack

Ich bin immer noch verwirrt. Wenn Du unter C++ als Rückgabewert eine Zeichenkette haben willst, dann wandel das Objekt in eine Python-Zeichenkette (`PyObject_Str()`) und von Da dann mit `PyString_AsString()` in eine C-Zeichenkette.
NoRulez
User
Beiträge: 36
Registriert: Donnerstag 1. November 2007, 15:31

Ich glaube da ist das Missverständnis :-)

Ich will es eben nicht als String behandeln, sondern als Datentyp, sprich wenn ich einen long Wert von Python bekomme, möchte ich diesen als Datentyp Long in C++ weiterverwenden. Wenn ich einen Double zurückbekomme dann eben mit double weiterarbeiten. Und genau das selbe will ich beim Übergeben an ein Python Skript. Ich will einen Int/Long/Double/... übergeben und nicht strings bzw. nicht NUR strings.

Lg NoRulez

P.S.: die printf anweisungen waren nur als beispiel gedacht
BlackJack

Bei der Rückgabe wirst Du dann wohl diese nette ``if/else if/…``-Kaskade benutzen müssen um die einzelnen Python-(Grund)Typen zu behandeln. C++ ist halt keine dynamisch typisierte Sprache.

Und bei Deinem ``printf()``-artigen Beispiel wirst Du auch irgendwie Typinformation selbst zusätzlich übergeben müssen. Muss man bei ``printf()`` ja auch machen. `Py_BuildValue()` ist da ganz nützlich.
Antworten