C-API: Pass a tuple to a method of a class

Stefan Behnel stefan_ml at behnel.de
Tue Jan 10 07:33:17 EST 2012


Hi!

pasparis at noos.fr, 10.01.2012 11:57:
> I am trying to pass a tuple to a method of a class from C++ to Python. I get a 
> Run Failed from the execution.
> thanks for help/suggestions

My *suggestion* is to use Cython instead of writing the glue code yourself.
There are several bugs and lots of inefficient data conversions in your
code below, which are clearly due to your lack of experience with CPython's
C-API. The good thing is that you don't need that experience if you use
Cython, because it will take care of all those details for you.


> the code is the following:
> 
> Python Code:
> 
> class cVector:
> def __init__(self,msg):
> self.value = msg
> def ComputeNorm(self,vecData):
> #don't use vecData for instance
> result = 12.
> return(result)

Please take care to properly format your code when you post it here. The
best way to do that is to post plain text messages.


> C++ Code :
> //instances. farenheit will hold our return value
> PyObject *ret, *mymod, *pclass, *method, *args, *object;
> float retValue;
> 
> Py_Initialize();
> //PySys_SetPath("/home/pascal/projPytCpp/proj1");
> PySys_SetPath(".");
> 
> // Module
> mymod = PyImport_ImportModule("mModule8");
> if (mymod == NULL){
> cout << "Can't Open a module:\n" ;
> Py_DECREF(mymod);

Note that Py_DECREF(null) will crash.

It looks like you are embedding Python here. Basically, the code up to this
point is all you really have to do. You can leave the rest to Cython,
especially the class instantiation and usage parts look much nicer in
Python syntax than in C-API code.


> // Class
> pclass = PyObject_GetAttrString(mymod, "cVector");
> if (pclass == NULL) {
> Py_DECREF(pclass);
> cout << "Can't find class\n";
> }
> 
> // Parameters/Values
> args = Py_BuildValue("(f)", 100.0);
> if (args == NULL) {
> Py_DECREF(args);
> cout << "Can't build argument list for class instance\n";
> }
> 
> // Object with parameter/value
> object = PyEval_CallObject(pclass, args);
> if (object == NULL) {
> Py_DECREF(object);
> cout << "Can't create object instance:\n";
> }
> 
> // Decrement the argument counter as we'll be using this again
> Py_DECREF(args);
> 
> // Get the object method - note we use the object as the object
> // from which we access the attribute by name, not the class
> method = PyObject_GetAttrString(object, "ComputeNorm");
> if (method == NULL) {
> Py_DECREF(method);
> cout << "Can't find method\n";
> }
> 
> // Decrement the counter for our object, since we now just need
> // the method reference
> Py_DECREF(object);
> 
> // Build our argument list - an empty tuple because there aren't
> // any arguments
> 
> cout << "Prepare the Tuple:\n" ;
> // WE pass a tuple
> args = PyTuple_New( 3 );
> if (args == NULL) {
> Py_DECREF(args);
> cout << "Can't build argument list for method call\n";
> }
> 
> PyObject *py_argument;
> // 1st argument
> py_argument = PyFloat_FromDouble(5.);
> PyTuple_SetItem(args, 0, py_argument);
> 
> // 2nd argument
> py_argument = PyFloat_FromDouble(10.);
> PyTuple_SetItem(args, 1, py_argument);
> 
> // 3nd argument
> py_argument = PyFloat_FromDouble(15.);
> PyTuple_SetItem(args, 2, py_argument);
> 
> cout << "Before the Exec:\n" ;
> // Call our object method with arguments
> //ret = PyEval_CallObject(method,args);
> ret = PyObject_CallObject(method,args);

Note that you are calling the method with three arguments here. It appears
that what you want is *one* argument instead, which happens to be a tuple.
So you need to wrap it in another tuple for calling. As I said, Cython will
do that for you.


> // Convert the return value back into a C variable and display it
> PyArg_Parse(ret, "f", &retValue);
> printf("Farenheit: %f\n", retValue);
> // Kill the remaining objects we don't need
> Py_DECREF(method);
> Py_DECREF(ret);
> // Close off the interpreter and terminate
> Py_Finalize();

That part is ok again.

Stefan




More information about the Python-list mailing list