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

时间:2018-08-15 02:51:18

标签: python c++ ctypes

我的问题基于此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 ++。

1 个答案:

答案 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

当然,通常最好设置argtypesrestype而不是猜测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进行测试)之外,我不知道我拥有哪个编译器版本。