4

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.

0

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

"It appears that the parameter to self is always NULL" is this a bug? Anyway issue resolved. Thanks bbastu.
I tried to figure out if it is a bug. All documentations and examples on the web that I found explicitly state, that self will be a pointer to the class instance. but none of them shows and example which uses the pointer. Is there anyone for whom self is not NULL?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.