我的问题基于此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;
};
}
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代码中,我想创建一个在printStuff
中调用mainFunc.cpp
的Python函数。我不需要在Python中初始化foo
的对象。但是,有两个问题:
(1)我是否正确使用了extern "C"
?
(2)如何将这三个文件编译为共享库?我用的是g ++。
答案 0 :(得分:2)
(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 -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 { {1}},最后将指向字符串缓冲区的指针的低32位用作const char *
。所以:
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
1。通过快速测试,对于x86_64 macOS,x86 macOS或x86_64 Linux,我并不需要它,但是PowerPC Linux需要import ctypes
foo = ctypes.cdll.LoadLibrary('foo.so')
foo.printStuff.argtypes = [ctypes.c_int]
foo.printStuff.restype = None
printStuff = foo.printStuff
,而ARM64 Linux需要-fpic
。但是,我实际上并没有运行所有这些。而且,除了macOS(我已确保在其中使用Homebrew gcc 8.2和Apple Clang 9.1进行测试)之外,我不知道我拥有哪个编译器版本。