如何用Cython包装C++类
How do I wrap a C++ class with Cython?
我有一个C++类。它由一个.ccp文件和一个.h文件组成。它可以编译(我可以编写一个在c++中成功使用它的主方法)。如何用Cython包装这个类以使其在Python中可用?
我已经阅读了文档,但没有遵循。他们讨论生成cpp文件。当我尝试遵循文档时,我已经存在的cpp会被吹走。。。
我应该在pyx文件中放入什么?我已经被告知了类的定义,但其中有多少?只是公开的方法?
我需要一个.pxd文件吗?我不知道什么时候需要或不需要这个文件。
我试过在#python IRC频道上问这些问题,但没有得到答案。
即使Cython通常与C一起使用,它也可以生成C++代码。编译时,添加--cplus
标志。
现在,为类创建包装器很简单,与包装结构没有太大区别。它主要不同于声明extern
,但这根本没有太大区别。
假设您在mycppclass.h
中有一个类MyCppClass
。
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
标志时可用,否则通过外部化<stdlib.h>
使用malloc
。
还要注意,您不需要取消引用指针(->
)来调用该方法。Cython跟踪对象的类型并应用适合的对象。
.pxd文件用于将声明与实现分离,或者避免命名空间冲突。想象一下,您想将Python包装器命名为C++类。只需将extern
声明放在.pxd文件中,将cimport
放在.pyx.中的pxd文件
cimport my_pxd
cdef my_pxd.SomeExternedType obj
请注意,不能在.pxd文件中编写实现。
所以,经过大量的戳、试错、尖叫和撕扯,我终于做到了。不过,首先,我必须将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根据它们的文件扩展名来计算如何编译它们。库数组告诉Cython编译器需要链接的内容(在本例中,我包装了一些加密内容,这些内容似乎无法通过现有的Python库直接模仿)。
为了使我的C函数在.pyx文件中可用,您可以在.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上)时,你会得到一个.,这样你就可以在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;
当然,您基本上必须以Python格式重新声明类的.h定义,但这对于访问已经编写的C++函数来说是一个很小的代价。
Cython主要用于C开发,要将C++与Python集成,我建议Boost.Python。他们出色的文档应该会让你很快上手。
以上答案或多或少回答了OP的问题。
但现在已经过了2020年年中,我想在这里(以资源摘要的形式)为那些想通过可以"pip安装"(或在PyPI中)的Python包来探索Python-C++绑定的人做一点贡献。
我认为,对于从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
- 如何在c++17中制作一个模板包装器/装饰器
- std::vector的包装器,使数组的结构看起来像结构的数组
- 如何在c++迭代器类型中包装std::chrono
- 是否可以用"iostream"包装现有的TCP/OOpenSSL会话
- 用pybind11包装C++抽象类时出错
- 为左值和右值的包装器实现C++范围
- C结构,其指针将被包装在unique_ptr中
- 如何包装第三方DLL在R中使用
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 将 N-arg 函数包装到另一个函数中
- 元组由 Swig 生成的 Python 包装器返回,用于C++向量
- 包装一个对象并假装它是一个 int
- 使用 Python Extension API 包装复杂C++类
- 外壳包装器句柄/执行交互式命令管道C++ UNIX
- 包装C++类时不完整的类型 GLFWwindow
- 将函数包装器转换为 std::function
- C++函数包装器来捕获某些信号
- 创建包装升压适配器的自定义范围类
- 如何包装(撰写)增强 hana 地图并访问括号运算符(运算符 [])?
- 使用运算符*重载包装矩阵乘法