c++ - Cython:如何包装公共(public)成员变量是自定义对象的 C++ 类?

标签 c++ cython

问题:

为了说明,我将使用 simple Rectangle tutorial in the Cython docs .假设我添加了一个自定义的 C++ 对象 Coord:

//Rectangle.h
class Coord {
public:
    int x, y;
    Coord(int x0, int y0);
    ~Coord();
    void move(int dx, int dy);

    //Omitted the rest for brevity...
};

然后我在 Rectangle 中创建两个公共(public)成员变量来表示矩形的左下角和右上角:

//Rectangle.h
class Rectangle {
public:
    Coord lower_left, upper_right;          //added this

    //Omitted the rest for brevity...
}; 

如果我想在 Python 中将 Coord 作为 python 对象来操作,我该怎么做?这似乎是一件很基本的事情,但我还没有在网上找到任何说明如何做到这一点的教程/指南。


我尝试了什么:

所以在我的尝试中,我尝试将 Coord 包装为 PyCoord 以便 PyRectangle 镜像 c++ 矩形对象。在定义中,我公开了 c++ Coord 并将它们添加为 Rectangle 的成员变量,如下所示:

#rect.pyx
cdef cppclass Coord:                        #added this cppclass
    Coord(int, int) except +
    void move(int, int)

cdef cppclass Rectangle:
    Rectangle(int, int, int, int) except +
    Coord lower_left, upper_right           #added these member variables

#The rest omitted for brevity...

这是我在包装器中尝试做的事情:

#rect.pyx
cdef class PyCoord:
    cdef Coord *thisptr

    def __cinit__(self):
        self.thisptr = new Coord(0,0)
    def __dealloc__(self):
        del self.thisptr
    def move(self, int dx, int dy):
        self.thisptr.move(dx, dy)

cdef class PyRectangle:
    cdef Rectangle *thisptr
    cdef PyCoord lower_left, upper_right    #want to manipulate these in Python

    def __cinit__(self, int x0, int y0, int x1, int y1):
        self.thisptr = new Rectangle(x0, y0, x1, y1)
        self.lower_left.thisptr = &(self.thisptr.lower_left)
        self.upper_right.thisptr = &(self.thisptr.upper_right)

#The rest omitted for brevity...

所以上面的第一个问题是当我重新分配 lower_leftupper_right 时存在内存泄漏,因为 new Coord(0,0) 从未被删除,对吧?但主要的想法是能够从 Python 中做这样的事情:

Python 解释器

>>> from rect import *
>>> a = PyRectangle(1,1,2,2)
>>> a.upper_right.move(1,1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'move'

但是如您所见,我遇到了问题。实现这一目标的最佳方法是什么?一定有一些标准的方法可以做到这一点,对吧?


编辑:所以看起来我可以访问像这样的原始 C++ 成员变量:

#rect.pyx
cdef cppclass Coord:
    int x, y                                #exposed x and y to cython
    Coord(int, int) except +
    void move(int, int)

cdef class PyCoord:
    cdef Coord *thisptr

    property c_x:                   #can use this to access C++ member x
        def __get__(self):
            return self.thisptr.x
        def __set__(self, int i):
            self.thisptr.x = i

    property c_y:                   #can use this to access C++ member y
        def __get__(self):
            return self.thisptr.y
        def __set__(self, int i):
            self.thisptr.y = i

    def __cinit__(self):
        self.thisptr = new Coord(0,0)
    def __dealloc__(self):
        del self.thisptr
    def move(self, int dx, int dy):
        self.thisptr.move(dx, dy)

因此,我可以在解释器中这样做:

Python 解释器

>>> from rect import *
>>> a = PyCoord()
>>> a.c_x = 1
>>> a.c_y = 2
>>> print ("(%s, %s)"%(a.c_x, a.c_y))
(1, 2)

但自定义对象类型仍然不走运。我无法使用 property 获取/设置 PyRectangle 中的 Coord,或者至少不知道如何正确执行此操作。

最佳答案

您需要一种包装Coord 的方法,它不会声明对内存的所有权。 PyCoord 的以下修改包含对实际所有者的引用,既表明 PyCoord 不应该释放内存,也为了让实际所有者保持事件状态,只要PyCoord 是活的。

cdef class PyCoord:
    cdef Coord *thisptr
    cdef object owner # None if this is our own

    def __cinit__(self):
        self.thisptr = new Coord(0,0)
        owner = None
    cdef set_ptr(self,Coord* ptr,owner):
        if self.owner is None:
            del self.thisptr
        self.thisptr = ptr
        self.owner = owner
    def __dealloc__(self):
        # only free if we own it
        if self.owner is None:
            del self.thisptr
    def move(self, int dx, int dy):
        self.thisptr.move(dx, dy)

您可以使用属性为 PyRectangle 提供漂亮的界面

cdef class PyRectangle:
    cdef Rectangle *thisptr

    def __cinit__(self, int x0, int y0, int x1, int y1):
        self.thisptr = new Rectangle(x0, y0, x1, y1)

    property lower_left:
        def __get__(self):
            c = PyCoord()
            # for some reason I couldn't do two levels of pointer access at once...
            # so we use a temporary
            thisptr = self.thisptr
            c.set_ptr(&thisptr.lower_left,self)
            return c
    # lower_right looks identical

注意:处理许多不同的对象所有权情况肯定是 Boost Python 考虑过的事情(我认为 SWIG 也考虑过)所以如果你要进行很多他们可能会提供一个稍微更优雅的解决方案。

关于c++ - Cython:如何包装公共(public)成员变量是自定义对象的 C++ 类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33764094/

相关文章:

python - Cython:numpy 数组的无符号整数索引给出不同的结果

python - 指定 cython 输出文件

c++ - 类型名称分解问题

c++ - 找不到图像

c++ - 我尝试用 C++ 编写一个 bmp 文件,但是 bmp 文件的宽度和高度似乎会影响结果,有时我会得到一个错误的 bmp 文件

c++ - std::list::sort 和指向元素的指针

c++ - c++ 中字符串的动态构造 - 什么是更 c++-ish 的方法来做到这一点?

python - Cython 中用于循环调用的快速基本线性代数

c++ - 我怎样才能在cython中调用这个函数?

c++ - 模板类中的 Cython C++ 静态方法