使用Ctypes调用复杂的c++代码
Using Ctypes to call complicated c++ code
我的问题是基于这个example。不同的是,在我的 C++ 代码中,我有一个函数可以在该函数 中启动自定义 class 的对象。在我的 python 代码中,我只想调用该函数。我不需要在Python中创建自定义class的对象。比如我的C++代码中有3个文件
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;
};
}
mainFunc.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) Did I use extern "C" correctly?
不要在方法定义周围放置 extern ”C"
。
extern "C"
的目的是告诉编译器某些函数需要作为 C 函数访问,即由 C 代码调用,或由 ctypes
或其他一些库调用从共享对象加载和调用函数。
您要通过 ctypes
调用的唯一函数是 printStuff
,因此只有那个函数应该是 extern "C"
。
尝试 extern "C"
这些方法可能不会对 g++
做任何无害的事情(它们最终仍会在导出 table 中命名为 __ZN3foo3barEv
之类的东西),但是您可能会收到警告,甚至是非 运行ning 库,使用不同的编译器。
(2) How should I compile these three files into a shared library? I use g++.
您可能想编写 Makefile
或使用其他构建系统,但如果您想手动执行:
g++ -shared -o foo.so foo.cpp mainFunc.cpp
根据您的平台,您可能还需要手动指定 -fPIC
or -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
和restype
比猜测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 进行测试)之外,我不知道我使用的是哪个编译器版本。
我的问题是基于这个example。不同的是,在我的 C++ 代码中,我有一个函数可以在该函数 中启动自定义 class 的对象。在我的 python 代码中,我只想调用该函数。我不需要在Python中创建自定义class的对象。比如我的C++代码中有3个文件
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;
};
}
mainFunc.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) Did I use extern "C" correctly?
不要在方法定义周围放置 extern ”C"
。
extern "C"
的目的是告诉编译器某些函数需要作为 C 函数访问,即由 C 代码调用,或由 ctypes
或其他一些库调用从共享对象加载和调用函数。
您要通过 ctypes
调用的唯一函数是 printStuff
,因此只有那个函数应该是 extern "C"
。
尝试 extern "C"
这些方法可能不会对 g++
做任何无害的事情(它们最终仍会在导出 table 中命名为 __ZN3foo3barEv
之类的东西),但是您可能会收到警告,甚至是非 运行ning 库,使用不同的编译器。
(2) How should I compile these three files into a shared library? I use g++.
您可能想编写 Makefile
或使用其他构建系统,但如果您想手动执行:
g++ -shared -o foo.so foo.cpp mainFunc.cpp
根据您的平台,您可能还需要手动指定 -fPIC
or -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
和restype
比猜测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 进行测试)之外,我不知道我使用的是哪个编译器版本。