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

时间:2015-11-17 18:11:35

标签: 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中创建了两个公共成员变量来表示矩形的左下角和右上角:

//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)

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

1 个答案:

答案 0 :(得分:0)

您需要一种包装Coord的方法,该方法不会声明对内存的所有权。 PyCoord的以下修改包含对实际所有者的引用,以指示PyCoord不应释放内存,并且只要{{1}保持实际所有者的存活活着。

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

注意:处理许多不同的对象所有权案例当然是Boost Python所考虑的事情(我认为SWIG也考虑过这一点)所以如果你要去很多这样他们可能会提供更优雅的解决方案。