Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

stodge

Variable argument list and PyObject_CallFunction

This topic is 5302 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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[i])
{
case ''c'':
AddItemToTuple(&tuple, va_arg(args, char));
break;
case ''i'':
AddItemToTuple(&tuple, va_arg(args, int));
break;
}
}

PyObject_CallFunctionObjArgs(functionObject, (PyObject*)tuple, NULL)

va_end(args);
DestroyTuple(&tuple);


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.

Share this post


Link to post
Share on other sites
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);
}
}

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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);
}
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!