如何使用ctypes的C ++类?

时间:2009-10-23 20:51:01

标签: c++ python ctypes

我刚刚开始使用ctypes,并且想使用ctypes从python中导出的dll文件中使用C ++类。 那么就说我的C ++代码看起来像这样:

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

我知道创建一个包含该类的.dll文件,然后使用ctypes在python中加载.dll文件。 现在我如何创建一个MyClass类型的Object并调用它的测试函数?这对ctypes来说甚至可能吗?或者我会考虑使用SWIG或Boost.Python,但ctypes似乎是小项目最简单的选择。

5 个答案:

答案 0 :(得分:37)

除了Boost.Python(对于需要将C ++类一对一地映射到python类的大型项目来说,这可能是一个更友好的解决方案),你可以在C ++端提供一个C接口。这是许多人的一个解决方案,因此它有自己的权衡,但我会为不熟悉该技术的人提供它。对于完全公开,使用这种方法不会将C ++连接到python,而是将C ++连接到C到Python。下面我提供了一个满足您要求的示例,向您展示C ++编译器的外部“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 ++库的功能并且不需要一对一映射,我会对此方法感到更兴奋。

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

答案 1 :(得分:36)

简短的故事是C ++没有标准的二进制接口,就像C语言一样。不同的编译器为同一个C ++动态库输出不同的二进制文件,因为名称错位和处理库之间堆栈的不同方式函数调用。

因此,遗憾的是,实际上没有一种可移植的方式来访问 。但是,对于一次一个编译器,它没有问题。

This blog post还简要概述了目前无效的原因。也许在C ++ 0x出来后,我们将有一个标准的C ++ ABI?在那之前,你可能无法通过Python ctypes访问C ++类。

答案 2 :(得分:5)

answer by AudaAero非常好但不完整(至少对我而言)。

在我的系统上(使用GCC和G ++ 6.3.0的Debian Stretch x64,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个代表点。

答案 3 :(得分:0)

这是关于如何在python中使用c和c ++与C_types的简短说明。 How to write a DLL/SO in C++ for Python

答案 4 :(得分:0)

扩展AudaAero'sGabriel Devillers回答我将通过以下方式完成类对象实例的创建: stdc=c_void_p(cdll.LoadLibrary("libc.so.6")) 使用ctypes c_void_p数据类型可确保在python中正确表示类对象指针。

还要确保dll处理dll的内存管理(dll中分配的内存也应该在dll中释放,而不是在python中释放)!