如何用Cython包装C ++类?

时间:2012-01-19 21:05:28

标签: c++ python python-2.7 cython

我有一个C ++类。它由一个.ccp文件和一个.h文件组成。它编译(我可以编写一个在c ++中成功使用它的main方法)。如何使用Cython将此类包装起来以使其在Python中可用?

我已经阅读了文档而没有关注。他们谈论生成cpp文件。当我试图关注文档时,我已经存在的cpp会被吹走......

我打算把它放在pyx文件中?我被告知课程定义,但有多少?只是公共方法?

我需要.pxd文件吗?我不明白这个文件何时需要或不需要。

我已经尝试在#python IRC频道中提出这些问题但无法得到答案。

4 个答案:

答案 0 :(得分:16)

即使Cython通常用于 C ,它也可以生成 C ++ 代码。编译时,添加--cplus标志。

现在,为类创建包装器很简单,与包装结构没有多大区别。它主要不同于声明extern,但根本没有太大区别。

假设您在MyCppClass中有一个班级mycppclass.h

cdef extern from "mycppclass.h":
    cppclass MyCppClass:
        int some_var

        MyCppClass(int, char*)
        void doStuff(void*)
        char* getStuff(int)

cdef class MyClass:

    # the public-modifier will make the attribute public for cython,
    # not for python. Maybe you need to access the internal C++ object from
    # outside of the class. If not, you better declare it as private by just
    # leaving out the `private` modifier.
    # ---- EDIT ------
    # Sorry, this statement is wrong. The `private` modifier would make it available to Python,
    # so the following line would cause an error es the Pointer to MyCppClass
    # couldn't be converted to a Python object.
    #>> cdef public MyCppClass* cobj
    # correct is:
    cdef MyCppClass* obj

    def __init__(self, int some_var, char* some_string):
        self.cobj = new MyCppClass(some_var, some_string)
        if self.cobj == NULL:
            raise MemoryError('Not enough memory.')

    def __del__(self):
        del self.cobj

    property some_var:
        def __get__(self):
            return self.cobj.some_var
        def __set__(self, int var):
            self.cobj.some_var = var

请注意,new关键字仅在设置--cplus标志时可用,否则使用malloc中的<stdlib.h>进行外围设置。

另请注意,您无需取消引用指针(->)即可调用该方法。 Cython跟踪对象的类型并应用适合的类型。

.pxd文件用于从实现中分离声明,或者用于避免命名空间冲突。想象一下,您想要像C ++类一样命名Python包装器。只需在.px文件中输入extern声明和cimport .pyx中的pxd文件。

cimport my_pxd
cdef my_pxd.SomeExternedType obj

请注意,您无法在.pxd文件中编写实现。

答案 1 :(得分:5)

经过大量的探索,试验和错误,尖叫和撕裂我的头发,我终于得到了这个工作。首先,我不得不将我的C ++重新编写成C语言,这对我来说实际上只涉及将所有std::string变量转换为char*并跟踪一些长度。

完成后,我有.h和.c文件。我想从Python中提供的C代码中创建一个函数。事实证明,Cython可以将你的C文件编译成你的扩展并一次性链接任何库,所以从我的setup.py开始,它看起来像这样:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules=[
  Extension("myext",
    ["myext.pyx", "../stuff.c"],
    libraries=["ssl", "crypto"]
  )
]

setup(
  name = "myext",
  cmdclass = {"build_ext": build_ext},
  ext_modules = ext_modules
)

正如您所看到的,Extension的第二个参数只列出了需要编译的所有文件,Cython根据文件扩展名计算出如何编译它们,据我所知。 libraries数组告诉Cython编译器需要链接什么(在这种情况下,我正在包装一些我似乎无法通过现有Python库直接模仿的加密内容)。

要实际在.pyx文件中使用我的C函数,可以在.pxd中编写一个小包装器。我的myext.pxd看起来如下:

cdef extern from "../stuff.h":
    char* myfunc(char* arg1, char* arg2, char* arg3)

在.pyx中,然后使用cimport声明导入此函数,然后可以使用该函数,就好像它是任何其他Python函数一样:

cimport myext

def my_python_func(arg1, arg2, arg3):
    result = myext.myfunc(arg1, arg2, arg3)
    return result

当你构建它时(至少在Mac上),你得到一个.so,你可以在python中导入并运行.pyx中的函数。可能有更好,更正确的方法来使这一切全部有效,但这来自经验,这是我设法解决的第一次遭遇。我对可能出错的指针非常感兴趣。

<强>更新

在进一步使用Cython之后,我发现将它与C ++集成起来非常简单,一旦你知道你在做什么。使C ++的string可用就像你的pyx / pyd中的from libcpp.string cimport string一样简单。声明C ++类同样容易:

cdef extern from "MyCPPClass.h":
    cdef cppclass MyCPPClass:
        int foo;
        string bar;

当然,您必须以Pythonic格式重新声明类的.h定义,但这对于访问已编写的C ++函数来说是一个很小的代价。

答案 2 :(得分:3)

Cython主要用于C开发,要将C ++与Python集成,我建议Boost.Python。他们出色的文档可以让你很快开始。

答案 3 :(得分:1)

上面的答案或多或少地回答了OP的问题。

但是它已经过去了2020年年中,我想为那些希望通过``pip安装''的python包来探索Python-C ++绑定的人做出贡献(以资源摘要的形式)(或在PyPI中)。

我认为是从Python调用C ++ /封装了第三方库,人们可以看看cppyy。它是一个相对较年轻的项目,但肯定看起来很有前途,并具有现代编译器的支持。请参阅下面的链接: https://cppyy.readthedocs.io/en/latest/

此外,总是有pybind11...。 https://github.com/pybind/pybind11

Cython还本地支持C ++(对于大多数C ++语言);有关更多信息,请参见https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html