从 Cython 模块导出包装的 C++ 类

Exporting wrapped C++ classes from Cython modules

假设我正在尝试包装 vector

module/__init__.pxd:

from libcpp.vector cimport vector
from libc.stdint cimport uint32_t

cdef class MyVector:
    cdef vector[uint32_t]* thisptr

module/__init__.pyx:

from libc.stdint cimport uint32_t
from libcpp.vector cimport vector
from cython.operator cimport dereference as deref

cdef class MyVector:
    # the field declaration is in *.pxd
    def __cinit__(self):
        self.thisptr = new vector[uint32_t]()
        self.thisptr.push_back(42)

    def __dealloc__(self):
        del self.thisptr
        self.thisptr = <vector[uint32_t]*> NULL

    def mysize(self):
        return self.thisptr.size()

    def myget(self):
        return deref(self.thisptr)[0]

module/__init__.pyxbldrun.pyxbld:

def make_ext(modname, pyxfilename):
    from distutils.extension import Extension
    return Extension(
        name=modname,
        sources=[pyxfilename],
        language='c++'
    )

run.pyx:

from module cimport MyVector

cdef main():
    obj = MyVector()
    print(obj.thisptr.size()) # 1
    print(obj.mysize()) # 1
    print(obj.myget())  # 42

main()

test.py:

import pyximport
pyximport.install()

import run

当我 运行 test.py 时,它崩溃并出现以下回溯:

Traceback (most recent call last):
  File "/usr/lib64/python3.4/site-packages/pyximport/pyximport.py", line 210, in load_module
    mod = imp.load_dynamic(name, so_path)
  File "module/__init__.pxd", line 5, in init run (/home/pastafarianist/.pyxbld/temp.linux-x86_64-3.4/pyrex/run.cpp:918)
    cdef class MyVector:
AttributeError: 'module' object has no attribute 'MyVector'

如果我将 module/__init__.pyx 移动到 module.pyx 并将 module/__init__.pxd 移动到 module.pxd,则相同的代码会起作用。我缺少什么以及如何解决它?

其他一些相关问题。

  1. 有没有什么方法可以将模板化包装器暴露给 Cython 代码,这样我就可以 MyVector[uint16_t] 而无需编写另一个包装器?
  2. 我为每个与 C++ 代码交互的源文件添加 pyxbld 文件是否正确?这是多余的吗? (我喜欢 pyximport 的便利,而且我不想每次都手动重新编译代码,而我仍在努力让它工作。)
  3. 如何将 module 编译为独立扩展? setup.py 应该是什么样子?
  4. 在上面的代码中,我在调用 C++ 方法之前从未使用过 deref。 Cython 如何理解我的意思 -> 和我的意思 .

对于这些问题中的任何一个,我将不胜感激。


UPD: 实际上,我在包装 Google 的 sparsehash,我想出了一个 way to do what I wanted,但它看起来像黑魔法。我仍然希望澄清此错误的原因以及如何正确编写 Cython 包装器。

您应该考虑使用 'swig'。该工具将创建您在 C++ 和 Python 之间进行交互所需的所有代码。 Link to swig website.

我在一个复杂的项目中使用了 swig,效果非常好。

无论如何,如果您不想在代码中使用此框架,可以使用 swig 创建的代码作为参考。

您的主要问题是 。因此,您可以通过制作一个空的 __init__.py 文件来解决问题。这一切感觉就像一个混乱的边缘案例。在您的原始代码中,MyVector class 永远不会添加到模块中,因此它无法找到您描述的错误。

您的其他问题的简短回答是:

  1. 否 - 不幸的是,没有构建模板化包装器的好方法 class。不过,您不需要为仅在 Cython 中使用的任何内容构建包装器 classes。

  2. 我认为这些最终会成为非常重要的答案,因此我将跳过它们。一般来说,setup.py 似乎比 pyximport 更受欢迎,文档中有关于如何使用它的很好的例子。

  3. Cython 在内部跟踪对象的类型。只要它知道它正在使用指针,它就会替换 -> ,否则替换 . 是相当简单的,所以正如你所说,你不必自己这样做。