如何在Cython C ++容器中存储python对象? [英] How to store python objects in Cython C++ containers?

查看:80
本文介绍了如何在Cython C ++容器中存储python对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想移植现有的库,其中的问题,并带有使用的问题的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.

  1. 将值存储在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

  1. 您可以将Python C-API和Wrapper一起使用以手动增加和减少引用.用于计数的Python C API参考是
  1. 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对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆