How to create a new instance, in C/C++ extension module, of a class defined in Python code?

StackOverflow https://stackoverflow.com/questions/16098271

  •  04-04-2022
  •  | 
  •  

문제

As the message subject says: How can I, in the C/C++ extension module, create a new instance of a class that's defined in Python code? I'll provide more info below.

NOTE#1: I'm trying to do this with Python/C APIs, as you'll see below. But any other alternative solutions are also acceptable for me~)

I have a Python file, CppInterop.py:

import CppProxy

class RealTimeData:
    def __init__(self, id, value):
        self.id = id
        self.value = value

def writeAll():
    dataList= []
    dataList.append(RealTimeData("ID::001", 1.0))
    dataList.append(RealTimeData("ID::002", 2.0))
    dataList.append(RealTimeData("ID::003", 3.0))
    count = CppProxy.WriteRealTimeDataList(dataList)
    print count

def readAll():
    dataList = CppProxy.ReadRealTimeDataList()  # Try to read the list back
    for data in dataList:
        print "id: ", data.id
        print "value: ", data.value
    pass

if __name__ == '__main__':
    writeAll()
    readAll()

In the "readAll" function I want to read the list stored in the C/C++ module back to Python code and then print every element.

It is expected the "dataList" returned from the CppProxy is a list that contains elements of class RealTimeData(as defined at the beginning).

To do so, I think in my C/C++ module extension code I need to create a PyList instance and then some instances of the RealTimeData. Might be something like this:

static
PyObject * ReadRealTimeDataList(PyObject * self, PyObject * args)
{
    // Create a new Python list, size of which is zero.
    PyObject * listObj = PyList_New(0);
    if (NULL == listObj)
    {
        return NULL;
    }

    // Append some elements to the list
    for (int i = 0; i < 3; ++i)
    {
        // How can I create a RealTimeData instance here and assign it to dataObj?
        PyObject * dataObj;

        PyObject * idObj = PyString_FromString("some id");
        PyObject_SetAttrString(dataObj, "id", idObj);

        PyObject * valueObj = PyFloat_FromDouble((double)i);
        PyObject_SetAttrString(dataObj, "value", valueObj);

        if (PyList_Append(listObj, dataObj) != 0)
        {
            // error!
            return NULL;
        }
    }

    // return the list object to Python part
    return Py_BuildValue("O", listObj);
}

I read this SO question. Do I need to do the same stuff as defining a new PyTypeObject to describe the Python class RealTimeData, and then create new instances of it?

But I think this actually introduces a new class type from C++ code, not re-using a class type that's already defined in Python code.

Any help is appreciated!

도움이 되었습니까?

해결책

A Python-defined class is instantiated in a Python/C extension exactly the same as in Python: by importing it and calling the class object:

// obtain the RealTimeData class
PyObject *get_RealTimeData()
{
    PyObject *module = PyImport_ImportModule("CppInterop");
    if (!module)
      return NULL;
    return PyObject_GetAttrString(module, "RealTimeData");
}

static
PyObject *ReadRealTimeDataList(PyObject *self, PyObject *args)
{
  static PyObject *RealTimeData = get_RealTimeData();
  ...

    // instantiate the class like you would in Python - by
    // calling the class object
    dataObj = PyObject_CallFunction(RealTimeData, "sd", "some id", 0.0);
    if (!dataObj)
      return NULL;
    ...
}

Several unrelated notes:

  • RealReadTimeDataList for some reason doesn't return the data list, but a single-element tuple that holds the list. Since your Python code expects the list itself, just return listObj instead.
  • Error checking is missing: the code doesn't check the result of PyString_FromString, PyFloat_FromDouble, or PyObject_SetAttrString.
  • Py_DECREF(listObj) should be called when PyList_Append fails. As written, the code would leak listObj in such a situation.
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top