使用 Ctypes 调用复杂的 c++ 代码

Posted

技术标签:

【中文标题】使用 Ctypes 调用复杂的 c++ 代码【英文标题】:Using Ctypes to call complicated c++ code 【发布时间】:2018-08-15 02:51:17 【问题描述】:

我的问题是基于这个example。不同的是,在我的 C++ 代码中,我有一个函数,它在该函数中启动自定义类的对象。在我的 python 代码中,我只想调用该函数。我不需要在 Python 中创建自定义类的对象。例如,我的 C++ 代码中有三个文件

foo.h

class foo 

public: 
        foo();
        void bar();
        int a;


;

foo.cpp

extern "C" 
    foo::foo () 
      a = 5;
    


    void foo::bar()
            std::cout << "Hello" << std::endl;
    ;

ma​​inFunc.cpp

extern "C" 
    void printStuff (int addNum) 
        foo fooArg;
        fooArg.a = fooArg.a + addNum;
        std::cout<<"Printing..."<<std::endl;
        std::cout<<fooArg.a<<std::endl;
    

在我的 Python 代码中,我想创建一个 Python 函数,该函数在 mainFunc.cpp 中调用 printStuff。我不需要在 Python 中启动 foo 的对象。但是,有两个问题:

(1) 我是否正确使用了extern "C"

(2)我应该如何将这三个文件编译成一个共享库?我用 g++。

【问题讨论】:

【参考方案1】:

(1) 我是否正确使用了 extern "C"?

不要在方法定义周围加上extern ”C"

extern "C" 的目的是告诉编译器某些函数需要可作为 C 函数访问,即由 C 代码、ctypes 或其他加载和调用函数的库调用共享对象。

您想通过ctypes 调用的唯一函数是printStuff,所以只有那个函数应该是extern "C"

尝试extern "C" 这些方法可能对g++ 无害(它们最终仍会在导出表中命名为__ZN3foo3barEv),但您可能会收到警告,甚至是非-运行库,使用不同的编译器。


(2)我应该如何将这三个文件编译成一个共享库?我用 g++。

您可能想编写Makefile 或使用其他构建系统,但如果您想手动执行:

g++ -shared -o foo.so foo.cpp mainFunc.cpp

根据您的平台,您可能还需要手动指定-fPIC or -fpic1

如果您想单独执行这些步骤:

g++ -c foo.cpp
g++ -c mainFunc.cpp
g++ -shared -o foo.so foo.o mainFunc.o

在这种情况下,如果需要 PIC 标志,则它位于前两行。


现在,从 Python 中使用它:

$ python3
>>> import ctypes
>>> foo = ctypes.cdll.LoadLibrary('foo.so')
>>> foo.printStuff(10)
Printing...
15
>>> foo.printStuff("abc")
Printing...
116480373

当然,通常最好设置argtypesrestype 而不是让ctypes 猜测。它猜测 Python int 10 应该转换为 C int 10,效果很好,但它也猜测 Python str "abc" 应该转换为 C const char *,然后你结束了将指向字符串缓冲区的指针的低 32 位用作int。所以:

>>> foo.printStuff.argtypes = [ctypes.c_int]
>>> foo.printStuff.restype = None
>>> foo.printStuff(10)
Printing...
15
>>> foo.printStuff("abc")
ArgumentError: argument 1: <class 'TypeError'>: wrong type

你可能想写一个 foo.py 来总结一下:

import ctypes
foo = ctypes.cdll.LoadLibrary('foo.so')
foo.printStuff.argtypes = [ctypes.c_int]
foo.printStuff.restype = None
printStuff = foo.printStuff

1。通过快速测试,我在 x86_64 macOS、x86 macOS 或 x86_64 Linux 上不需要它,但 PowerPC Linux 需要 -fpic 而 ARM64 Linux 需要 -fPIC。但是,我实际上并没有运行所有这些。而且,除了 macOS(我确保使用 Homebrew gcc 8.2 和 Apple Clang 9.1 进行测试)之外,我不知道我有哪个编译器版本。

【讨论】:

很好的答案。在我的机器上,我必须使用-fPIC 进行编译。你必须这样做吗?

以上是关于使用 Ctypes 调用复杂的 c++ 代码的主要内容,如果未能解决你的问题,请参考以下文章

使用 ctypes 返回值

使用 ctypes 在 python 中使用 C++ 库

如何使用 ctypes 停止和重新启动从 python 运行的 C++ 代码

使用ctypes在Python中调用C++动态库

Python混合编程:C语言接口ctypes

Python Ctypes,C++ 字符串到浮点数的转换在 python 中调用 Matplotlib 后给出错误/舍入的结果