问题:
为了说明,我将使用 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_left
和 upper_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/