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 related to the "cythonfunc" in rect.pyx. This is intended to be a public function that can be called from main with a rectangle struct/object as parameter, and return an area to main.cpp.
I've tried c struct and python object, both does not work for me. If I use this codes, the compiler gives an error of
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 error of:
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 only success with passing separate x0, y0, x1, y1 as parameter to cythonfunc. Is there a correct way to passing a cpp struct/object or python object as parameter to a cython public function?
With respect to your second attempt (which is probably the more sensible way of calling it from C++, although I'd pass by reference):
cdef public int cythonfunc(Rectangle c_rect):
result = c_rect.getArea()
return result
the issue is that it doesn't know what Rectangle
is, since the Cython generated rect.h
doesn't include Rectangle.h
. The easiest way to fix this is to swap the order of the includes in main.cpp
:
#include "Rectangle.h" // this one is now first
#include "rect.h"
The error "shapes has not been declared" was telling you this...
With respect you your first attempt, you were right that you have to make PyRectangle
public. To do so you also need to specify a "type object name" as Cython tells you. You do so as shown here (although it honestly isn't hugely clear...):
cdef public class PyRectangle [object c_PyRect, type c_PyRect_t]:
# ... as before
This ensures that PyRectangle is available to C/C++ as struct c_PyRect
(and so the cythonfunc
signature is int cythonfunc(struct c_PyRect *);
.)
Also, the Python TypeObject
defining PyRectangle
is available as c_PyRect_t
, but you don't need that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With