python - 使用 Ctypes 调用复杂的 c++ 代码
问题描述
我的问题是基于这个例子。不同的是,在我的 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;
};
}
主函数.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 代码中,我想创建一个调用printStuff
. mainFunc.cpp
我不需要foo
在 Python 中启动一个对象。但是,有两个问题:
(1) 我使用extern "C"
正确吗?
(2)我应该如何将这三个文件编译成共享库?我使用 g++。
解决方案
(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
或-fpic
。1
如果您想单独执行这些步骤:
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
当然,通常最好设置argtypes
andrestype
而不是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 进行测试)之外,我不知道我拥有哪个编译器版本。
推荐阅读
- c# - 如何在 ASP Web Api 中将多个键传递给数组?
- javascript - 如何在鼠标位置跟踪的父元素内移动子元素
- git - 如何更新 Github 克隆存储库?
- ruby-on-rails - 调用切片!使用 map / symbol_to_proc 语法
- react-admin - 在 React-admin 转换方法中,为什么无法获取当前状态?
- filter - 在 Dataweave 2.0 中使用两个或更多列值比较两个 JSON 数组
- python - Pandas groupby 并添加带有随机数据的新行
- sql - SQL GROUP BY 奇怪的行为
- mern - 如何使用 node、express 和 mongoose 制作简单的 RESTful 应用程序
- powershell - 使用 powershell 显示来自日志文件的完整错误消息