Cython扩展类:如何在自动生成的C结构中公开方法?

时间:2016-01-20 17:12:34

标签: python c++ cython

我有现有的C ++代码,它定义了我需要使用的一些类,但我需要能够将这些类发送到Python代码。具体来说,我需要在C ++中创建类实例,创建Python对象作为这些C ++对象的包装器,然后将这些Python对象传递给Python代码进行处理。这只是一个更大的C ++程序的一部分,因此需要使用C / Python API最终在C ++中完成。

为了让我的生活更轻松,我使用Cython来定义扩展类(cdef类),它们充当我的C ++对象的Python包装器。我使用的典型格式是cdef类包含指向C ++类的指针,然后在创建cdef类实例时对其进行初始化。因为如果我有一个现有的C ++对象要包装,我也希望能够替换指针,我已经将我的cdef类的方法添加到accept() C ++对象并获取其指针。我的其他cdef类在Cython中成功使用accept()方法,例如当一个对象拥有另一个对象时。

以下是我的Cython代码示例:

MyCPlus.pxd

cdef extern from "MyCPlus.h" namespace "mynamespace":
    cdef cppclass MyCPlus_Class:
        MyCPlus_Class() except +

PyModule.pyx

cimport MyCPlus
from libcpp cimport bool

cdef class Py_Class [object Py_Class, type PyType_Class]:
    cdef MyCPlus.MyCPlus_Class* thisptr
    cdef bool owned

    cdef void accept(self, MyCPlus.MyCPlus_Class &indata):
        if self.owned:
            del self.thisptr
        self.thisptr = &indata
        self.owned = False

    def __cinit__(self):
        self.thisptr = new MyCPlus.MyCPlus_Class()
        self.owned = True

    def __dealloc__(self):
        if self.owned:
            del self.thisptr

当我尝试从C ++访问accept()方法时出现问题。我尝试在我的cdef类和public方法上使用apiaccept()关键字,但我无法弄清楚如何在Cython的自动生成的{C结构中公开此方法{1}}文件。无论我尝试什么,C结构都是这样的:

.h(由Cython自动生成)

PyModule.h

我还尝试将struct Py_Class { PyObject_HEAD struct __pyx_vtabstruct_11PyModule_Py_Class *__pyx_vtab; mynamespace::MyCPlus_Class *thisptr; bool owned; }; 输入键入为self,我甚至尝试使用Py_ClassPy_Class关键字向前声明public。我还尝试将api作为静态方法。我尝试过的任何东西都没有用于公开accept()方法,以便我可以在C ++中使用它。我尝试通过accept()访问它,但是我遇到了编译器错误,“无效使用不完整类型”。我搜索了很多,但没有看到解决方案。谁能帮我?拜托,谢谢!

3 个答案:

答案 0 :(得分:2)

正如您在comment中指出的那样,__pyx_vtab成员似乎仅供Cython使用,因为它甚至不会在导出的标头中为其定义结构类型(或多个)。

添加回复,一种方法也可以是:

cdef api class Py_Class [object Py_Class, type Py_ClassType]:
    ...
    cdef void accept(self, MyCPlus.MyCPlus_Class &indata):
        ...  # do stuff here
    ...


cdef api void (*Py_Class_accept)(Py_Class self, MyCPlus.MyCPlus_Class &indata)
Py_Class_accept = &Py_Class.accept

基本上,我们定义一个函数指针并将其设置为我们想要公开的扩展方法。这与您的回复的cdef功能没有多大区别;主要区别在于我们可以像往常一样在类定义中定义我们的方法,而不必复制功能或对另一个函数的方法/函数调用来公开它。需要注意的是,我们除了self的扩展类型之外,几乎逐字地定义我们的函数指针的签名(在这种情况下) ) 等等;那么这也适用于常规功能。

请注意我在C级Cython .pyx文件上尝试了这个,我没有,并且不打算在CPP实现文件上测试它。但是我希望这可能会起到同样的作用。

答案 1 :(得分:1)

这不是一个真正的解决方案,但我想出了解决问题的方法。我仍然希望有一个解决方案,允许我告诉Cython将accept()方法暴露给C ++。

我的解决方法是我为Python类(不是方法)编写了一个单独的函数。然后我将api关键字提供给我的Python类和新函数:

cdef api class Py_Class [object Py_Class, type PyType_Class]:
(etc.)

cdef api Py_Class wrap_MyCPlusClass(MyCPlus.MyCPlus_Class &indata):
    wrapper = Py_Class()
    del wrapper.thisptr
    wrapper.thisptr = &indata
    wrapper.owned = False
    return wrapper

对于我需要包装的不同类的数量,这有点笨拙,但至少Cython将该函数放在易于使用的API中:

struct Py_Class* wrap_MyCPlusClass(mynamespace::MyCPlusClass &);

答案 2 :(得分:0)

在声明cpdef时,您可能希望使用cdef代替accept。见the docs

  

可以从Python和C中调用   *使用cpdef语句声明   *可以从任何地方调用,因为它使用了一点Cython魔法   *从其他Cython代码调用时使用更快的C调用约定。

试试吧!