Adding __add__ operator to Cython wrapper class?
The following C ++ code breaks away from the standard Cython rectangle example with an arbitrary "+" operator added:
#include "Rectangle.h"
using namespace shapes;
Rectangle::Rectangle(int X0, int Y0, int X1, int Y1)
{
x0 = X0;
y0 = Y0;
x1 = X1;
y1 = Y1;
}
Rectangle::~Rectangle() {}
int Rectangle::getLength()
{
return (x1 - x0);
}
Rectangle operator+(const Rectangle &r1, const Rectangle &r2)
{
return Rectangle(r1.X0 + r2.X0, r1.Y0 + r2.Y0, r1.X1 + r2.X1, r1.Y1 + r2.Y1)
}
This refers to the Cython C ++ class definition:
cdef extern from "Rectangle.h" namespace "shapes":
cdef cppclass Rectangle:
Rectangle(int, int, int, int) except +
int x0, y0, x1, y1
int getLength()
Rectangle operator+(Rectangle) nogil
The only way we decided to do it was the following Cython code:
cdef class PyRectangle:
cdef Rectangle *thisptr # hold a C++ instance which we're wrapping
def __cinit__(self, int x0=0, int y0=0, int x1=0, int y1=0):
if x0 == 0:
self.thisptr = NULL
else:
self.thisptr = new Rectangle(x0, y0, x1, y1)
def __dealloc__(self):
del self.thisptr
def getLength(self):
return self.thisptr.getLength()
def __add__(self, other):
cdef Rectangle rect = deref(self.thisptr) + deref(other.thisptr)
cdef Rectangle* ptr_rect = new Rectangle(rect.x0, rect.y0, rect.x1, rect.y1)
ret = PyRectangle()
ret.thisptr = ptr_rect
return ret
This is not very optimal as we have an extra copy in __add__
and the code is not very simple / short too. This is for wrapping an external library, so we cannot just define any new constructors for the Rectangle, and we cannot rewrite the addition at the Cython level.
We thought we could just write something like:
ret = PyRectangle() deref(ret.thisptr) = deref(self.thisptr) + deref(other.thisptr) return ret
But it gives the error "Cannot assign or remove this".
Is there a better way to do this kind of thing in Keaton? The solution we found is not viable in our code.
source to share
For pointers, it is the x[0]
same as deref(x)
, so you can write
ret.thisptr[0] = self.thisptr[0] + other.thisptr[0]
Note also that if the wrapped object has a nullable constructor then there is no need for pointers, just do
cdef class PyRectangle:
cdef Rectangle c_rect
def __init__(self, int x0=0, int y0=0, int x1=0, int y1=0):
self.c_rect = Rectangle(x0, y0, x1, y1)
# no __dealloc__ needed
def __add__(PyRectangle left, PyRectangle right):
PyRectangle ret = PyRectangle()
ret.c_rect = left.c_rect + right.c_rect
return ret
It is convenient for me in this case to add a static method
cdef class PyRectangle:
[...]
@staticmethod
cdef create(Rectangle r):
PyRectangle ret = PyRectangle()
ret.c_rect = r
return ret
then you can just do
def __add__(PyRectangle left, PyRectangle right):
return PyRectangle.create(left.c_rect + right.c_rect)
source to share