Variable argument list and PyObject_CallFunction

Started by
7 comments, last by stodge 20 years, 2 months ago
Is it possible to pass a variable argument list to Py_BuildValue and then PyObject_CallFunction? I''m trying to create a generic function that receives the following parameters: - module name - function name - parameter list format (e.g. "if") - parameter list void PythonScriptManager::execute(const String& module, const String& function, const char* args, ...) It invokes the Python function "function", and passes the parameters it receives, with no early knowledge as to the number or types of parameters? But I can''t see a way of passing these parameters into Py_BuildValue and PyObject_CallFunction. I realise this might not make much sense! Thanks
---------------------http://www.stodge.net
Advertisement
Ok this is what I have so far:

	void PythonScriptManager::execute(const String& function, const char* args, ...)	{		va_list vl;		int i;		int argc = 0;		PyObject *arglist;		PyObject *result;		PyObject *mod, *func;		PyObject*	p[5];		for (i=0;i<5;i++)		{			p[i] = NULL;		}		//  args is the last argument specified; all		//   others must be accessed using the variable-		//   argument macros.		va_start( vl, args );		// Step through the list.		for( i = 0; args[i] != ''\0''; ++i )		{			union Printable_t			{				int     i;				float   f;				char    c;				char   *s;				bool b;			} Printable;			argc++;			switch( args[i] )    // Type to expect.			{			case ''i'':				Printable.i = va_arg( vl, int );				printf( "param=%i\n", Printable.i );				p[i] = Py_BuildValue("i", Printable.i);				break;			case ''f'':				Printable.f = va_arg( vl, float );				printf( "param=%f\n", Printable.f );				p[i] = Py_BuildValue("f", Printable.f);				break;				/*			case ''c'':				Printable.c = va_arg( vl, char );				printf( "%c\n", Printable.c );				lua_pushstring(L, Printable.c);				break;				*/			case ''b'':				Printable.b = va_arg( vl, bool );				printf( "param=%u\n", Printable.b );				p[i] = Py_BuildValue("b", va_arg( vl, bool ));				break;			case ''s'':				Printable.s = va_arg( vl, char * );				printf( "param=%s\n", Printable.s );				p[i] = Py_BuildValue("s", va_arg( vl, char* ));				break;			default:				break;			}		}		va_end( vl );		std::cout << "Argument count = " << argc << std::endl;		mod = PyImport_ImportModule("main");		if (mod == NULL)		{			LogManager::getSingleton().logMessage(LML_NORMAL,						"Error executing Python entry point");		}		else		{			func = PyDict_GetItemString(PyModule_GetDict(mod), function.c_str());			result = PyObject_CallFunction(func, (char*)args,				p[0] == NULL ? p[0] : Py_None,				p[1] == NULL ? p[1] : Py_None,				p[2] == NULL ? p[2] : Py_None,				p[3] == NULL ? p[3] : Py_None,				p[4] == NULL ? p[4] : Py_None				);		}		Py_DECREF(result);		Py_DECREF(mod);		//Py_DECREF(arglist);		for (int jj=0; jj<argc; jj++)		{			Py_DECREF(p[jj]);		}	}


It will invoke the Python function, when I call:

execute("fred", "i", 198);

The Python function is:

def fred(g):	print "Inside fred g=", g	return


The variable g has some whacky value of 504131448 though.

Any suggestions appreciated. THanks
---------------------http://www.stodge.net
If Im getting this right, you have a function with a variable length argument list, and you want to pass this variable amount of arguments to a Python function?

Well I would suggest that you also pass a string showing the data type of the nth term (''c'' for char, ''i'' for integer and so on, ''^'' to signify the end). Then you could do a for loop through the string, something along these lines (pseudo-code)

pytuple* tuple;va_list args;CreateTuple(&tuple);va_start(args);for(int i = 0; stringTypes != ''^''; i++){  switch(stringTypes)<br>  {<br>    case ''c'':<br>      AddItemToTuple(&tuple, va_arg(args, char));<br>      break;<br>    case ''i'':<br>      AddItemToTuple(&tuple, va_arg(args, int));<br>      break;<br>  }<br>}<br><br>PyObject_CallFunctionObjArgs(functionObject, (PyObject*)tuple, NULL)<br><br>va_end(args);<br>DestroyTuple(&tuple);<br> </pre> <br><br>This probably isn''t what you''re looking for, nor would it be a really good solution if it is. It would mean that your python function would have to receive a tuple also. Maybe you could pass a dictionary instead and that would be a bit more practical. I hope you can fill in the parts of that code that are incorrect, and if this isn''t feasible, maybe it could give you some ideas.  </i>  
I guess I posted more info while you were writing your response.

Thanks!
---------------------http://www.stodge.net
Good idea. I''m almost there, except the call to PyObject_CallObject crashes:

	void PythonScriptManager::execute(const String& function, int count, const char* args, ...)	{		va_list vl;		int i;		int argc = 0;		PyObject *arglist;		PyObject *result;		PyObject *mod, *func;		PyObject* pArgs;		PyObject* pValue;		//  args is the last argument specified; all		//   others must be accessed using the variable-		//   argument macros.		va_start( vl, args );		pArgs = PyTuple_New(count); // max of 5 parameters		// Step through the list.		for( i = 0; args[i] != ''\0''; ++i )		{			union Printable_t			{				int     i;				float   f;				char    c;				char   *s;				bool b;			} Printable;			argc++;			switch( args[i] )    // Type to expect.			{			case ''i'':				Printable.i = va_arg( vl, int );				printf( "param=%i\n", Printable.i );				pValue = PyInt_FromLong(Printable.i);				PyTuple_SetItem(pArgs, i, pValue);				Py_DECREF(pValue);				break;			case ''f'':				Printable.f = va_arg( vl, float );				printf( "param=%f\n", Printable.f );				pValue = PyFloat_FromDouble(Printable.f);				PyTuple_SetItem(pArgs, i, pValue);				Py_DECREF(pValue);				break;							case ''b'':				Printable.b = va_arg( vl, bool );				printf( "param=%u\n", Printable.b );				break;			case ''s'':				Printable.s = va_arg( vl, char * );				printf( "param=%s\n", Printable.s );				break;			default:				break;			}		}		va_end( vl );		mod = PyImport_ImportModule("main");		if (mod == NULL)		{			LogManager::getSingleton().logMessage(LML_NORMAL,						"Error executing Python entry point");		}		else		{			func = PyDict_GetItemString(PyModule_GetDict(mod), function.c_str());			result = PyObject_CallObject(func, pArgs);		}		Py_DECREF(pArgs);		if (pValue != NULL)		{			Py_DECREF(pValue);		}		Py_DECREF(mod);		if (result != NULL)		{			Py_DECREF(result);		}	}
---------------------http://www.stodge.net
Or you could use Py_VaBuildValue(char*, va_list).

Edit: just add a pair of () around your format string so that the value being built is packed into a tuple.

“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan (C programming language co-inventor)

[edited by - Fruny on February 7, 2004 10:34:19 PM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Thanks Fruny; I researched your suggestion and it works!

It''s pretty messy but here it is:

	void PythonScriptManager::execute(	const String& module,										const String& function,										const char* format, ...)	{		va_list arguments;		int i;		int argc = 0;		PyObject *arglist;		PyObject *result;		PyObject *pModule , *func;		PyObject* pArgs;		PyObject* pValue;		PyObject* pName;		//  args is the last argument specified; all		//   others must be accessed using the variable-		//   argument macros.		va_start(arguments, format);		pArgs = Py_VaBuildValue((char*)format,arguments);		if (pArgs == NULL)		{			LogManager::getSingleton().logMessage(LML_NORMAL,						"Error creating argument tuple");			return;		}		va_end(arguments);		if (!PyTuple_Check(pArgs))		{			LogManager::getSingleton().logMessage(LML_NORMAL,						"Error - argument is not a tuple");			PyObject *a;			a = PyTuple_New(1);			if (a == NULL)				return;			if (PyTuple_SetItem(a, 0, pArgs) < 0)				return;			pArgs = a;		}		pName = PyString_FromString(module.c_str());		pModule  = PyImport_Import(pName);		if (pModule  == NULL)		{			LogManager::getSingleton().logMessage(LML_NORMAL,						"Error executing Python entry point");			return;		}		Py_DECREF(pName);		func = PyDict_GetItemString(PyModule_GetDict(pModule ), function.c_str());		result = PyObject_CallObject(func, pArgs);		if (!result)		{			PyErr_Print();		}		if (result != NULL)		{			Py_DECREF(pArgs);		}		if (result != NULL)		{			Py_DECREF(pModule);		}		if (result != NULL)		{			Py_DECREF(result);		}	}
---------------------http://www.stodge.net
quote:Original post by Fruny
Or you could use Py_VaBuildValue(char*, va_list).

Edit: just add a pair of () around your format string so that the value being built is packed into a tuple.


I was checking the documentation, it doesn''t seem to be listed there (or maybe my documentation is just old). Intersting though, maybe Ill be able to make use of that myself.

quote:Original post by porthios
I was checking the documentation, it doesn''t seem to be listed there (or maybe my documentation is just old). Intersting though, maybe Ill be able to make use of that myself.


It''s not listed in the doc, but it''s listed in the header files

“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan (C programming language co-inventor)
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan

This topic is closed to new replies.

Advertisement