How to create a public cython function that can take a C ++ struct / instance or python object as a parameter?

My Rectangle.h

namespace shapes { class Rectangle { public: int x0, y0, x1, y1; Rectangle(); Rectangle(int x0, int y0, int x1, int y1); ~Rectangle(); int getArea(); }; } 

My Rectangle.cpp

 #include "Rectangle.h" namespace shapes { Rectangle::Rectangle() { } Rectangle::Rectangle(int X0, int Y0, int X1, int Y1) { x0 = X0; y0 = Y0; x1 = X1; y1 = Y1; } Rectangle::~Rectangle() { } int Rectangle::getArea() { return (x1 - x0) * (y1 - y0); } } 

My rect.pyx

 # distutils: language = c++ # distutils: sources = Rectangle.cpp cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() cdef class PyRectangle: cdef Rectangle c_rect def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() cdef public int cythonfunc(PyRectangle py_rect): result = py_rect.get_area() return result 

My main.cpp

 #include <Python.h> #include "rect.h" #include "Rectangle.h" #include <iostream> int main (int argc, char *argv[]) { int result; Py_Initialize(); PyInit_rect(); shapes::Rectangle c_rect = shapes::Rectangle(0,0,2,1); result = cythonfunc(c_rect); std::cout<<result<<"\n"; Py_Finalize(); return 0; } 

My Makefile

 all: cython3 --cplus rect.pyx c++ -g -O2 -c rect.cpp -o rect.o `python3-config --includes` c++ -g -O2 -c Rectangle.cpp -o Rectangle.o `python3-config --includes` c++ -g -O2 -c main.cpp -o main.o `python3-config --includes` c++ -g -O2 -o rect Rectangle.o rect.o main.o `python3-config --libs` clean: rm -f rect rect.cpp rect.h *.o 

My problem is with "cythonfunc" in rect.pyx. It is intended for a public function that can be called from main using a struct or object in the form of a rectangle as a parameter and returns the main.cpp area.

I tried c struct and python object, both do not work for me. If I use these codes, the compiler will throw an error

 Error compiling Cython file: ------------------------------------------------------------ ... def __cinit__(self, int x0, int y0, int x1, int y1): self.c_rect = Rectangle(x0, y0, x1, y1) def get_area(self): return self.c_rect.getArea() cdef public int cythonfunc(PyRectangle py_rect): ^ ------------------------------------------------------------ rect.pyx:19:27: Function declared public or api may not have private types 

So, I added "public" to PyRectangle, but got another error:

 Error compiling Cython file: ------------------------------------------------------------ ... Rectangle() except + Rectangle(int, int, int, int) except + int x0, y0, x1, y1 int getArea() cdef public class PyRectangle: ^ ------------------------------------------------------------ rect.pyx:12:5: Type object name specification required for 'public' C class 

If I change cythonfunc to:

 cdef public int cythonfunc(Rectangle c_rect): result = c_rect.getArea() return result 

I got an error:

 In file included from main.cpp:3:0: rect.h:21:42: warning: 'cythonfunc' initialized and declared 'extern' __PYX_EXTERN_C DL_IMPORT(int) cythonfunc(shapes::Rectangle); ^ rect.h:21:42: error: 'shapes' has not been declared main.cpp: In function 'int main(int, char**)': main.cpp:17:29: error: 'cythonfunc' cannot be used as a function result = cythonfunc(c_rect); ^ 

I can succeed only by passing individual x0, y0, x1, y1 as a parameter to cythonfunc. Is there a way to pass a cpp struct / object or python object as a parameter to a cython public function?

+5
source share
1 answer

As for your second attempt (probably this is a more reasonable way to call it from C ++, although I would follow the link):

 cdef public int cythonfunc(Rectangle c_rect): result = c_rect.getArea() return result 

the problem is that he does not know what Rectangle , because the created Cython rect.h does not include Rectangle.h . The easiest way to fix this is to replace the inclusion order in main.cpp :

 #include "Rectangle.h" // this one is now first #include "rect.h" 

The error "form was not declared" told you this ...


As for your first attempt, you were right that you need to make PyRectangle publicly available. To do this, you also need to specify an โ€œobject type nameโ€, as Cython tells you. You do it as shown here (although this is honestly not very clear ...):

 cdef public class PyRectangle [object c_PyRect, type c_PyRect_t]: # ... as before 

This ensures that PyRectangle is accessible to C / C ++ as struct c_PyRect (and therefore the signature cythonfunc int cythonfunc(struct c_PyRect *); )

Additionally, the Python TypeObject defining the PyRectangle is available as c_PyRect_t , but you don't need it.

+1
source

Source: https://habr.com/ru/post/1259663/


All Articles