Search code examples
c++operator-overloadingcythonfriend

Wrapping C++ friend non-member operator in Cython


I am a newbie for both c++ and cython, and confused about wrapping a C++ friend non-member operator in Cython. Here is a tiny example that I am trying to wrap, but failed. Much Appreciate

Now, how can I declare the friend operator in pyx file

all files can be found here, makefile for test -

Rectangle.h

 namespace shapes {
    class Rectangle {
      public:
        int x0, y0, x1, y1;
        Rectangle(int x0=0, int y0=0, int x1=0, int y1=0);
        ~Rectangle();
        int getLength();
        Rectangle operator+(const Rectangle& target);
        friend Rectangle operator-(const Rectangle & left, const Rectangle & right);
    };
    }

Rectangle.cpp

#include "Rectangle.h"

using namespace shapes;

Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) {
    x0 = X0;
    y0 = Y0;
    x1 = X1;
    y1 = Y1;
}


int Rectangle::getLength() {
    return (x1 - x0);
}

Rectangle::~Rectangle()
{

}

Rectangle Rectangle::operator+(const Rectangle & target) {
    return Rectangle(x0+target.x0, y0+target.y0,x1+target.x1,y1+target.y1);
}

Rectangle operator-(const Rectangle & left,const Rectangle & right) {
    return Rectangle(left.x0 - right.x0,
                     left.y0-right.y0,
                     left.x1-right.x1,
                     left.y1-right.y1);
}

pyx file

from libcpp.vector cimport vector
from cython.operator cimport dereference as deref 

# c++ interface to cython
cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Rectangle:
        Rectangle() except +
        Rectangle(int, int, int, int) except +
        int x0, y0, x1, y1
        int getLength()
        Rectangle opadd "operator+"(Rectangle right)
        Rectangle opsub "operator-" (Rectangle right)
     #  Rectangle opsub "operator-"(Rectangle left ,Rectangle right)

# cdef extern from "Rectangle.h" namespace "shapes":
#     cdef Rectangle opsub "operator-"(Rectangle left ,Rectangle right) 

# creating a cython wrapper class
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):
        self.thisptr = new Rectangle(x0, y0, x1, y1)
    def __dealloc__(self):
        del self.thisptr
    def getLength(self):
        return self.thisptr.getLength()
    def __add__(PyRectangle left,PyRectangle right):
        cdef Rectangle rect = left.thisptr.opadd(right.thisptr[0])
        cdef PyRectangle sum = PyRectangle(rect.x0,rect.y0,rect.x1,rect.y1)
        return sum
    def __sub__(PyRectangle left,PyRectangle right):
        cdef Rectangle rect = left.thisptr.opsub(right.thisptr[0])
        # cdef Rectangle rect = opsub(left.thisptr[0],right.thisptr[0])
        cdef PyRectangle sub = PyRectangle(rect.x0,rect.y0,rect.x1,rect.y1)
        return sub
    def __repr__(self):
        return "PyRectangle[%s,%s,%s,%s]" % (
                self.thisptr.x0,
                self.thisptr.y0,
                self.thisptr.x1,
                self.thisptr.y1)

I have tried serval approaches, such as

cdef extern from "Rectangle.h" namespace "shapes":
    cdef Rectangle opsub "operator-"(Rectangle left ,Rectangle right) 

or just pretend it to be a member-defined operator inside the cppclass declaration

Rectangle opsub "operator-" (Rectangle right)

I failed to compile both ways, as it shows

error: no member named 'operator-' in 'shapes::Rectangle'

Solution

  • Sorry, I found a big mistake in the Rectangle.cpp. The definition of the operator- needs to be prefixed with namespace

        Rectangle shapes::operator-(const Rectangle & left,const Rectangle & right) {
        return Rectangle(left.x0 - right.x0,
                         left.y0-right.y0,
                         left.x1-right.x1,
                         left.y1-right.y1);
    }
    

    And rect.pyx can be modified to call operator directly according to hints from DavidW.

        # c++ interface to cython
    cdef extern from "Rectangle.h" namespace "shapes":
        cdef cppclass Rectangle:
            Rectangle() except +
            Rectangle(int, int, int, int) except +
            int x0, y0, x1, y1
            int getLength()
            Rectangle operator+(Rectangle right)
            Rectangle operator-(Rectangle right)
    
    ####  nonmember operator can also work :
    # cdef extern from "Rectangle.h" namespace "shapes":
        # cdef Rectangle operator-(Rectangle left ,Rectangle right) 
    
    # creating a cython wrapper class
    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):
            self.thisptr = new Rectangle(x0, y0, x1, y1)
        def __dealloc__(self):
            del self.thisptr
        def getLength(self):
            return self.thisptr.getLength()
        def __add__(PyRectangle left,PyRectangle right):
            cdef Rectangle rect = left.thisptr[0] + right.thisptr[0]
            cdef PyRectangle sum = PyRectangle(rect.x0,rect.y0,rect.x1,rect.y1)
            return sum
        def __sub__(PyRectangle left,PyRectangle right):
            cdef Rectangle rect = left.thisptr[0] - right.thisptr[0]
            # cdef Rectangle rect = opsub(left.thisptr[0],right.thisptr[0])
            cdef PyRectangle sub = PyRectangle(rect.x0,rect.y0,rect.x1,rect.y1)
            return sub