python - 如何在 Cython C++ 容器中存储 python 对象?

标签 python c++ templates cython

我想移植一个现有的 图书馆与 到 Python,使用 C++ 库 .在这种情况下,它是 adevs library .

问题是如何使用 Cython 在 C++ 容器中存储 Python 对象?我知道这是不知何故 discouraged ,对于引用计数的问题,但是可以这样做吗?如果可以,如何做?

我知道 Gauthier Boaglios' answersimilar question .但是,这显然没有解决引用计数的问题,因为我尝试了以下操作:

假设在“cadevs.pxd”中,我有以下代码:

cdef extern from "adevs/adevs_digraph.h" namespace "adevs":
cdef cppclass PortValue[VALUE, PORT]:
    PortValue() except +
    PortValue(PORT port, const VALUE& value) except +
    PORT port
    VALUE value

在“advs.pyx”中:
from cpython.ref cimport PyObject
cimport cadevs

ctypedef PyObject* PythonObject

cdef class PortValue:
    cdef cadevs.PortValue[PythonObject, PythonObject]* _c_portvalue

    def __cinit__(self, object port, object value):
        self._c_portvalue = new cadevs.PortValue[PythonObject, PythonObject](
            <PyObject *>port, <PyObject *>value
        )

    def __dealloc__(self):
        del self._c_portvalue

    property port:
        def __get__(self):
            return <object>self._c_portvalue.port

    property value:
        def __get__(self):
            return <object>self._c_portvalue.value

然后我cythonize并编译
$ cython --cplus -3 adevs.pyx
$ g++ -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python3.4m -I../include -lpython3.4m -o adevs.so adevs.cpp

但是在 python 或 ipython 中运行
import adevs
pv = adevs.PortValue((1,2), 3)
pv.port
pv.port

显然,由于对 (1, 2) 元组的引用丢失,Python 崩溃。

最佳答案

您是对的,通过使用 Cython 将 python 对象存储在 C++ 容器中,您将难以运行内存安全应用程序。如果您想在 Cython 中执行此操作,而不是 Pybind11(如 Mike MacNeil 的回答所引用),那么您有多种选择。

  • 将值存储在 Cython/Python 中的某个位置,以在对象位于容器中时将引用计数保持在 1 以上。示例:
  • cdef class PortValue:
        cdef cadevs.PortValue[PythonObject, PythonObject]* _c_portvalue
    
        # Add fields to keep stored Python objects alive.
        cdef object port_ref_holder 
        cdef object value_ref_holder 
    
    
        def __cinit__(self, object port, object value):
            self._c_portvalue = new cadevs.PortValue[PythonObject, PythonObject](
                <PyObject *>port, <PyObject *>value
            )
    
            # Assign objects here to keep them alive.
            port_ref_holder = port
            value_ref_holder = value
    
  • 您可以使用 Python C-API 和 Wrapper 来手动递增和递减引用。用于引用计数的 Python C API 引用是 here . cython 包在 Cython 中自动向您提供此 API 作为 cython 声明 (.pxd) 文件,您可以将其导入(请参阅 here )。我可以在单独的 C++ 文件中添加引用计数功能,或者我可以根据 Interfacing with C guide 将此代码直接逐字添加到 Cython .像这样的事情是一个开始:
  • from cpython.ref cimport PyObject, Py_INCREF, Py_DECREF
    cdef extern from *:
        """
        class PyRef {
            PyObject* obj;
        public:
            
            PyObject* get() {return obj;}
            PyRef() {obj = NULL;}
            PyRef(PyObject* set_obj) {
                Py_XINCREF(set_obj); 
                obj = set_obj;}
            
            ~PyRef() {
                Py_XDECREF(obj);obj = NULL;
            }
    
            PyRef(const PyRef& other)  {
                Py_XINCREF(other.obj); 
                obj = other.obj;
            }
            PyRef(PyRef&& other) {obj = other.obj; other.obj = NULL;}
    
            PyRef& operator=(const PyRef& other) {
                Py_XDECREF(obj); 
                Py_XINCREF(other.obj); 
                obj = other.obj;
                return *this;
            }
            PyRef& operator=(PyRef&& other) {
                Py_XDECREF(obj); 
                obj = other.obj; 
                other.obj = NULL;
                return *this;
            }
        };
        """
        cdef cppclass PyRef:
            PyRef() except +
            PyRef(PyObject* set_obj) except +
            PyObject* get() except +
        
    
    然后使用“PyRef”类而不是 PythonObject 并使用其 get() 方法借用对存储的 Python 对象的引用。

    关于python - 如何在 Cython C++ 容器中存储 python 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26937213/

    相关文章:

    python - reshape numpy 数组

    python - Python 中的随机生成器函数

    c++ - 如何使用 C++ 以二进制模式将 vector 写入文件?

    python - 静态与动态链接需要建议

    C++ 递归函数类型

    c++ - 所有子类的模板特化

    python - 在 python 中从一个列表创建两个列表?

    python - 无法检测到在 Ubuntu 上安装包的 Python 路径

    templates - 调用没有尖括号的模板函数可以吗?

    templates - 将conf文件嵌入到helm图表中