如何创建一个可以接收c struct / instance或python对象作为参数的公共cython函数?

我的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();
    };
}

我的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);
    }
}

我的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

我的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;
}

我的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

我的问题与rect.pyx中的“cythonfunc”有关.这是一个公共函数,可以从main调用矩形struct / object作为参数,并返回一个区域到main.cpp.

我试过c struct和python对象,两者都不适合我.如果我使用此代码,编译器会给出错误

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

所以我向PyRectangle添加了“public”,但又出现了另一个错误:

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

如果我将cythonfunc更改为:

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

我得到的错误是:

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);
                             ^

我只能将单独的x0,y0,x1,y1作为参数传递给cythonfunc.是否有正确的方法将cpp结构/对象或python对象作为参数传递给cython公共函数?

最佳答案
关于你的第二次尝试(这可能是从C调用它的更明智的方式,虽然我通过引用传递):

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

问题是它不知道Rectangle是什么,因为Cython生成的rect.h不包含Rectangle.h.解决这个问题的最简单方法是在main.cpp中交换包含的顺序:

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

错误的“形状尚未宣布”告诉你这个……

尊重你的第一次尝试,你必须公开PyRectangle.为此,您还需要指定一个“类型对象名称”,如Cython告诉您的那样.你按照here所示这样做了(尽管它真的不是很清楚……):

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

这确保了PyRectangle可以作为struct c_PyRect用于C/C++(因此cythonfunc签名是int cythonfunc(struct c_PyRect *);.)

此外,定义PyRectangle的Python TypeObject可用作c_PyRect_t,但您不需要它.

转载注明原文:如何创建一个可以接收c struct / instance或python对象作为参数的公共cython函数? - 代码日志