如何与ctypes一起使用C ++类?

我刚刚开始使用ctypes,并希望使用C ++类,我已经使用ctypes在python内导出的dll文件。 所以可以说我的C ++代码看起来像这样:

class MyClass { public: int test(); ... 

我会知道创build一个包含此类的.dll文件,然后使用ctypes在python中加载.dll文件。 现在我将如何创build一个MyClasstypes的对象并调用它的testing函数? 这甚至可能与ctypes? 或者我会考虑使用SWIG或Boost.Python,但ctypes似乎是小项目的最简单的select。

简言之,就是没有标准的C ++二进制接口,就像C语言一样。不同的编译器为同一个C ++dynamic库输出不同的二进制文件,这是由于名称改变以及在库函数调用之间处理堆栈的不同方法。

所以,不幸的是,通常没有一种可移植的方式来访问C ++库。 但是,一次编译一个编译器就没有问题了。

这个博客文章还简要介绍了为什么这个目前不行。 也许在C ++ 0x出来之后,我们将有一个标准的C ++的ABI? 在那之前,你可能没有办法通过Python的ctypes访问C ++类。

除了Boost.Python(对于需要C ++类到python类的一对一映射的较大项目,这可能是一个更友好的解决scheme),您可以在C ++端提供一个C接口。 这是许多方面的解决scheme,所以它有自己的权衡,但我会介绍它为不熟悉技术的人的利益。 为了全面的披露,使用这种方法不需要将C ++连接到Python,而是将C ++连接到Python。 下面我列出了一个满足您的要求的例子,向您展示C ++编译器的extern“c”工具的总体思路。

 //YourFile.cpp (compiled into a .dll or .so file) #include <new> //For std::nothrow //Either include a header defining your class, or define it here. extern "C" //Tells the compile to use C-linkage for the next scope. { //Note: The interface this linkage region needs to use C only. void * CreateInstanceOfClass( void ) { // Note: Inside the function body, I can use C++. return new(std::nothrow) MyClass; } //Thanks Chris. void DeleteInstanceOfClass (void *ptr) { delete(std::nothrow) ptr; } int CallMemberTest(void *ptr) { // Note: A downside here is the lack of type safety. // You could always internally(in the C++ library) save a reference to all // pointers created of type MyClass and verify it is an element in that //structure. // // Per comments with Andre, we should avoid throwing exceptions. try { MyClass * ref = reinterpret_cast<MyClass *>(ptr); return ref->Test(); } catch(...) { return -1; //assuming -1 is an error condition. } } } //End C linkage scope. 

你可以编译这个代码

 gcc -shared -o test.so test.cpp #creates test.so in your current working directory. 

在你的python代码中,你可以做这样的事情(从2.7所示的交互式提示):

 >>> from ctypes import cdll >>> stdc=cdll.LoadLibrary("libc.so.6") # or similar to load c library >>> stdcpp=cdll.LoadLibrary("libstdc++.so.6") # or similar to load c++ library >>> myLib=cdll.LoadLibrary("/path/to/test.so") >>> spam = myLib.CreateInstanceOfClass() >>> spam [outputs the pointer address of the element] >>> value=CallMemberTest(spam) [does whatever Test does to the spam reference of the object] 

我确定Boost.Python在底层做了类似的事情,但是也许理解底层的概念是有帮助的。 如果您尝试访问C ++库的function并且不需要一对一映射,那么我会对此方法更感兴趣。

有关C / C ++交互的更多信息,请查看Sun的此页面: http : //dsc.sun.com/solaris/articles/mixing.html#cpp_from_c

AudaAero的答案非常好,但并不完整(至less对我而言)。

在我的系统上(使用GCC和G ++ 6.3.0,Python 3.5.3的Debian Stretch x64)当我调用访问类的成员值的成员函数时,我有段错误。 我通过将指针值打印到stdout来进行诊断,在Python中用64位来编码的void *指针在Python中被表示为32位。 因此,当它传递回成员函数包装时会发生很大的问题。

我find的解决scheme是改变:

 spam = myLib.CreateInstanceOfClass() 

 Class_ctor_wrapper = myLib.CreateInstanceOfClass Class_ctor_wrapper.restype = c_void_p spam = c_void_p(Class_ctor_wrapper()) 

所以缺less两件事情:设置返回types为c_void_p(默认为int) 然后创build一个c_void_p对象(不只是一个整数)。

我希望我能写评论,但是我仍然缺乏27个重点。

这是关于如何在python中使用c和c ++与C_types的简短解释。 如何在C ++中为Python编写DLL / SO

扩展AudaAero的和Gabriel Devillers答案我会完成类对象实例创build: stdc=c_void_p(cdll.LoadLibrary("libc.so.6"))使用ctypes c_void_p数据types确保在python中的类对象指针的正确表示。

还要确保dll的内存pipe理由dll处理(dll中分配的内存也应该在dll中解除分配,而不是在python中)!