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

时间:2015-11-30 09:18:45

标签: python c++ wrapper cython

假设我试图包裹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.pyxmodule/__init__.pxd移至module.pxd,则相同的代码有效。我错过了什么,我该如何解决?

其他一些相关问题。

  1. 有没有办法将模板化的包装器暴露给Cython代码,这样我可以MyVector[uint16_t]而不需要编写另一个包装器?
  2. 我是否为每个与C ++代码交互的源文件添加pyxbld文件?这是多余的吗? (我喜欢pyximport的便利性,我不想每次手动重新编译代码,而我仍然试图让它工作。)
  3. 如何将module编译为独立扩展程序? setup.py应该是什么样的?
  4. 在上面的代码中,我在调用C ++方法之前从未使用deref。 Cython如何理解我的意思->以及我的意思.
  5. 我很感激您对这些问题的帮助。

    UPD:实际上,我正在包装Google的sparsehash,我已经找到了way to do what I wanted,但它看起来像是黑魔法。我仍然希望澄清这个错误发生了什么,以及如何正确编写Cython包装器。

2 个答案:

答案 0 :(得分:2)

您的主要问题是the code in __init__.so (generated from __init__.pyx) does not get executed unless you also have __init__.py (which does not get executed in this case)。因此,您可以通过创建一个空的__init__.py文件来解决问题。这一切都像一个凌乱的边缘情况。在原始代码中,MyVector类永远不会添加到模块中,因此无法通过您描述的错误找到它。

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

  1. 不 - 遗憾的是,没有好的方法来构建模板化的包装类。您不需要为只在Cython中使用的任何东西构建包装类。

  2. 我认为这些最终会成为非常重要的答案,所以我正在跳过它们。 setup.py似乎比pyximport更受欢迎,文档中提供了如何使用它的好例子。

  3. Cython在内部跟踪对象的类型。只要它知道它使用指针而->,就可以用.代替@Api( name="myApiName", version = "v1", namespace = @ApiNamespace(ownerDomain = "test.com", ownerName = "test.com", packagePath = ""), scopes = {Constants.EMAIL_SCOPE, Constants.PLUS_SCOPE}, clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID, Constants.IOS_CLIENT_ID}, audiences = {Constants.ANDROID_AUDIENCE} ) ,这是相当简单的,因为你说你不必自己这样做。

答案 1 :(得分:-1)

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

我在一个复杂的项目中使用了swig,它运行得非常好。

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