问题描述
我想移植现有的c ++库,其中 cython 的问题,并带有使用 templates 的问题的C ++库.在这种情况下,它是 adevs库.
I would like to port an existing c++ library with cython to Python, with the C++ library employing templates. In this case, it is the adevs library.
问题是如何使用Cython将Python对象存储在C ++容器中?对于参考计数问题,我知道受阻但还是可以完成,如果可以,怎么办?
The question is how can I store Python objects in a C++ container with Cython? I know this is somehow discouraged, for issues of reference counting, but can it be done nevertheless and if so, how?
我知道 Gauthier Boaglios对类似的问题.但是,显然,这并没有解决引用计数的问题,因为我尝试了以下操作:
I am aware of Gauthier Boaglios' answer to a similar question. However, this does not address the issue of reference counting, apparently, as I tried the following:
在"cadevs.pxd"中说,我有以下代码:
Let's say in 'cadevs.pxd' I have the following code:
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
在"adevs.pyx"中:
And in 'adevs.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和编译
Then I cythonize and compile
$ 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中运行
But running in python or ipython
import adevs
pv = adevs.PortValue((1,2), 3)
pv.port
pv.port
显然,Python失去了对(1,2)元组的引用.
crashes Python as the reference to the (1, 2) tuple is lost, apparently.
推荐答案
您是正确的,因为将Python对象存储在带有Cython的C ++容器中,您将在运行内存安全的应用程序时遇到困难.如果您想在Cython而不是Pybind11(如Mike MacNeil的答案所引用)中执行此操作,则有很多选择.
You are right in that you will have difficulties with running a memory-safe application by storing python objects in a C++ container with Cython. If you want to do this in Cython, and not Pybind11 (as referenced by Mike MacNeil's answer), then you have a number of options.
- 将值存储在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参考是此处.cython软件包会自动将cython声明(.pxd)文件作为您可以导入的cython声明(.pxd)文件在Cython中为您提供(请参见此处).我可以在单独的C ++文件中添加引用计数功能,也可以根据与C指南接口.像这样的事情是一个开始:
- You can use the Python C-API along with Wrapper to manually increment and decrement the reference The Python C API reference for reference counting is here. The cython package provides this API to you in Cython automatically as cython declaration (.pxd) file that you can cimport (see here). I can add reference counting functionality in a separate C++ file, or I can add this code directly to Cython verbatim according to the Interfacing with C guide. Something like this is a start:
来自cpython.ref的
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对象的引用.
Then you use the "PyRef" class instead of PythonObject and use its get() method to borrow a reference to the stored python object.
这篇关于如何在Cython C ++容器中存储python对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!