问题
I ask your help, because I'm trying to run a cython example a bit more complex than the one with one class that is possible to find in many tutorials (e.g. this guide ). I haven't found any "more advanced" tutorial, so I hope this question will be useful also for people that are trying to learn it a bit more in depth.
I will write here the steps I took, hoping that someone will tell me where is my mistake.
I have a Rectangle c++ class (I put here just the .h file to make it shorter):
#ifndef RECTANGLE_H
#define 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();
};
}
#endif
and a Group2 class. A very simple example class, whose constructor take as input 2 Rectangles:
#ifndef GROUP4_H
#define GROUP4_H
#include "Rectangle.h"
namespace shapes{
class Group2 {
public:
Rectangle rect0, rect1, rect2, rect3 ;
Group2();
Group2(Rectangle rect0, Rectangle rect1);
~Group2();
void getAreas(int *area0, int *area1);
};
}
#endif
Then I create a grp2.pyx file with the definition of both Rectangle and Group2 class:
#RECTANGLE
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()
# GROUP2
cdef extern from "Group2.h" namespace "shapes":
cdef cppclass Group2:
Group2() except +
Group2(Rectangle rect0, Rectangle rect1) except +
void getAreas(int *area0, int *area1)
cdef class PyGroup2:
cdef Group2 c_group2
def __cinit__(self, Rectangle rect0, Rectangle rect1):
self.c_group2 = Group2(rect0, rect1)
def get_areas(self):
cdef int area0, area1
self.c_group2.getAreas(&area0, &area1)
return area0, area1
Then I compile those two class in a static c++ library with command line:
gcc -c -fPIC Group2.cpp Rectangle.cpp
and
ar rcs libexample.a Group2.o Rectangle.o
To finish i create the cython setup.py file that i call from command line:
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(ext_modules = cythonize(Extension(
name="grp2", # the extension name
sources=["grp2.pyx"], # the Cython source and
libraries=["example"],
library_dirs=["lib"],
include_dirs=["lib"],
# additional C++ source files
language="c++", # generate and compile C++ code
)))
At this point I have the error in the _cinint_ of PyGroup2:
Cannot convert Python object argument to type 'Rectangle'
I suppose there is some mistake in my pyx file, but I cannot tell what, since I'm defining there the Rectangle for python.
回答1:
You should use PyRectangle
in the signatures of def
-functions and PyRectangle.c_rect
when passing rectangles to C++-functions.
That means your code should be:
cdef class PyGroup2:
...
def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
self.c_group2 = Group2(rect0.c_rect, rect1.c_rect)
Read on for a more detailed explanation why.
All arguments passed to def
-functions are Python-objects (i.e. of type object
in Cython-parlance), after all those functions will be called from pure Python, which only knows Python-objects.
However, you can add some syntactic sugar and use "late-binding" in the signature of a def
-function, for example, instead of
def do_something(n):
...
use
def do_something(int n):
...
Under the hood, Cython will transform this code to something like:
def do_something(n_):
cdef int n = n_ # conversion to C-int
...
This automatic conversion is possible for builtin-types like int
or double
, because there is functionality in Python-C-API for these conversions (i.e. PyLong_AsLong, PyFloat_AsDouble). Cython also handles the error checking, so you should not undertake these conversion manually.
However, for user-defined types/classes like your Rectangle
-class such automatic conversion is not possible - Cython can only automatically convert to cdef
-classes/extensions, i.e. PyRectangle
, thus PyRectangle
should be used in the signature:
cdef class PyGroup2:
...
def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
...
After Cython took care of conversion from object
to PyRectangle
, the last step from PyRectangle
to Rectangle
must be taken manually by utilizing the c_rect
- pointer:
...
def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
self.c_group2 = Group2(rect0.c_rect, rect1.c_rect)
The rules are similar for cpdef
-function, because they can be called from pure Python. The "early binding" works only for types which Cython can automatically coverted from/to Python objects.
Unsurprisingly, the only function which can habe C++-classes in their signatures are the cdef
-functions.
来源:https://stackoverflow.com/questions/52262669/cython-example-with-more-than-one-class