I was trying to create a Python class definition in C++ code and access it in Python. However, the function is called but the parameters are not received correctly. Please help me in doing this properly.
#include <iostream>
#include <Python.h>
using namespace std;
#include <Python.h>
static PyObject* MyClass__Init(PyObject *self, PyObject *args)
{
cout << "MyClass__Init Called" << endl;
Py_INCREF(Py_None);
return Py_None;
};
static PyObject* MyModule__Start(PyObject *self, PyObject *args)
{
const char* zBuff;
if (PyArg_ParseTuple(args, "s", &zBuff))
cout << "MyModule Start Called with parameter " << zBuff << endl;
else
cout << "MyModule Start ERROR" << endl;
Py_INCREF(Py_None);
return Py_None;
};
static PyObject* MyClass__Start(PyObject *self, PyObject *args)
{
const char* zBuff;
if (PyArg_ParseTuple(args, "s", &zBuff))
cout << "MyClass Start Called with parameter" << zBuff << endl;
else
cout << "MyClass Start ERROR" << endl;
Py_INCREF(Py_None);
return Py_None;
};
static PyMethodDef pModuleMethods[] =
{
{"Start", MyModule__Start, METH_VARARGS, ""},
{NULL, NULL, 0, NULL}
};
static PyMethodDef pClassMethods[] =
{
{"__init__", MyClass__Init, METH_VARARGS, ""},
{"Start", MyClass__Start, METH_VARARGS, ""},
{NULL, NULL, 0, NULL}
};
void Start()
{
Py_Initialize();
/* create a new module and class */
PyObject *pClassDic = PyDict_New();
PyObject *pClassName = PyString_FromString("MyClass");
PyObject *pClass = PyClass_New(NULL, pClassDic, pClassName);
PyObject *pModule = Py_InitModule("MyModule", pModuleMethods);
PyObject *pModuleDic = PyModule_GetDict(pModule);
/* add methods to class */
for (PyMethodDef* pDef = pClassMethods; pDef->ml_name != NULL; pDef++)
{
PyObject *pFunc = PyCFunction_New(pDef, NULL);
PyObject *pMethod = PyMethod_New(pFunc, NULL, pClass);
PyDict_SetItemString(pClassDic, pDef->ml_name, pMethod);
}
PyDict_SetItemString(pModuleDic, "MyClass", pClass);
PyRun_SimpleString("import MyModule\n"
"MyModule.Start('Hello Module')\n"
"myObj = MyModule.MyClass()\n"
"myObj.Start('Hello Class')\n");
Py_Finalize();
};
int main()
{
Start();
};
Output is,
MyModule Start Called with parameter Hello Module
MyClass__Init Called
MyClass Start ERROR
The Module function is called without any issue, but the class method is called without the proper input variable.
It appears that the parameter to self is always NULL
and instead - for class methods - the reference to self
is passed within the argument list.
So, to parse the arguments of a class method you need to parse the reference to self
, too.
Since Python 2.6 you can provide a list of format specifiers to PyArg_ParseTuple
. The number of format specifiers has to fit the number of arguments that are passed to the function (see https://docs.python.org/2/c-api/arg.html under (items) (tuple) [matching-items]
).
By modifying your MyClass__Start
function to parse an additional parameter, you are able to parse and print both arguments to inspect them.
For me, the following code
static PyObject* MyClass__Start(PyObject *self, PyObject *args)
{
PyObject* argListSelf;
const char* zBuff;
if (PyArg_ParseTuple(args, "Os", &argListSelf, &zBuff)) {
cout << "MyClass Start Called with parameters " <<
cout << PyString_AsString(PyObject_Str(argListSelf)) <<
cout << " and " << zBuff << endl;
cout << "self " << PyString_AsString(PyObject_Str(self)) << endl;
}
else {
if(PyErr_Occurred())
PyErr_Print();
cout << "MyClass Start ERROR" << endl;
}
Py_INCREF(Py_None);
return Py_None;
};
results in
MyModule Start Called with parameter Hello Module
MyClass__Init Called
MyClass Start Called with parameters \
0x602428<?.MyClass instance at 0x7f484a333200>0x602428 and HelloClass
self <NULL>
Note that I printed the pointer value of self, which is NULL
.
I also added
if(PyErr_Occurred())
PyErr_Print();
which I would always add for debugging purposes.