我刚刚开始使用ctypes,并希望使用C++类,我已经使用ctypes在python中导出了一个dll文件.那么就说我的C++代码看起来像这样:
class MyClass {
public:
int test();
...
我会知道创建一个包含该类的.dll文件,然后使用ctypes在python中加载.dll文件.现在我如何创建一个MyClass类型的Object并调用它的测试函数?这对ctypes来说甚至可能吗?或者我会考虑使用SWIG或Boost.Python,但ctypes似乎是小项目最简单的选择.
除了Boost.Python(对于需要将C++类一对一映射到python类的大型项目来说,它可能是一个更友好的解决方案),您可以在C++端提供一个C接口.它是许多人的一个解决方案,因此它有自己的权衡,但我会为不了解该技术的人提供它.对于完全公开,使用这种方法不会将C++连接到python,而是将C++连接到C到Python.下面我提供了一个满足您要求的示例,向您展示C++编译器的外部"c"工具的一般概念.
//YourFile.cpp (compiled into a .dll or .so file) #include//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 (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++库的功能并且不需要一对一映射,我会对此方法感到更兴奋.
有关C/C++交互的更多信息,请查看Sun的此页面:http://dsc.sun.com/solaris/articles/mixing.html#cpp_from_c
简短的故事是,有在路上对C没有标准二进制接口++,有针对相同的C++动态库C.不同的编译器输出不同的二进制文件,由于名字改编,不同的方式来处理库函数调用的堆栈.
所以,很遗憾,实在是没有访问C++库可移植的方式一般.但是,对于一次一个编译器,它没有问题.
这篇博文还简要概述了为什么这个目前不起作用.也许在C++ 0x出来后,我们将有一个标准的C++ ABI?在那之前,你可能无法通过Python访问C++类ctypes
.
AudaAero的答案非常好但不完整(至少对我而言).
在我的系统上(Debian Stretch x64 with GCC and G ++ 6.3.0,Python 3.5.3)我在调用访问该类成员值的成员函数时就会有段错误.我通过将指针值打印到stdout来诊断,在包装器中64位编码的void*指针在Python中以32位表示.因此,当它传递回成员函数包装器时会出现大问题.
我找到的解决方案是改变:
spam = myLib.CreateInstanceOfClass()
成
Class_ctor_wrapper = myLib.CreateInstanceOfClass Class_ctor_wrapper.restype = c_void_p spam = c_void_p(Class_ctor_wrapper())
因此缺少两件事:将返回类型设置为c_void_p(默认为int),然后创建c_void_p对象(不仅仅是整数).
我希望我能写一个评论,但我仍然缺少27个代表点.