Python与c++通信,作者描述了Python与c++相互通信的方法,依据文章所述,做了相关的实验,在此转述作者文章并记录自己的学习过程。
1. Python调用c++(基础篇)
2. Python调用c++(高级篇,使用swig工具)

Python调用c++

作者:Jerry Jho
链接:https://www.zhihu/question/23003213/answer/56121859
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这种做法称为Python扩展。比如说,我们有一个功能强大的C函数:int great_function(int a) {
return a + 1;
}
期望在Python里这样使用:

from great_module import great_function
great_function(2)
3

考虑最简单的情况。我们把功能强大的函数放入C文件 great_module.c 中。

#include <Python.h>

int great_function(int a) {
    return a + 1;
}

static PyObject * _great_function(PyObject *self, PyObject *args)
{
    int _a;
    int res;

    if (!PyArg_ParseTuple(args, "i", &_a))
        return NULL;
    res = great_function(_a);
    return PyLong_FromLong(res);
}

static PyMethodDef GreateModuleMethods[] = {
    {
        "great_function",
        _great_function,
        METH_VARARGS,
        ""
    },
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initgreat_module(void) {
    (void) Py_InitModule("great_module", GreateModuleMethods);
}

除了功能强大的函数great_function外,这个文件中还有以下部分:

  • 包裹函数_great_function。它负责将Python的参数转化为C的参数(PyArg_ParseTuple),调用实际的great_function,并处理great_function的返回值,最终返回给Python环境。
  • 第三个参数的含义是参数变长,第四个参数是一个说明性的字符串。导出表总是以{NULL, NULL, 0, NULL}结束。
  • 导出函数initgreat_module。这个的名字不是任取的,是你的module名称添加前缀init。导出函数中将模块名称与导出表进行连接。

从上述代码可以窥见Python内部运行的方式:

  • 所有Python元素,module、function、tuple、string等等,实际上都是PyObject。C语言里操纵它们,一律使用PyObject *。
  • 也可以创建Python类型的变量,使用PyXXX_New可以创建类型为XXX的变量。
  • 若a是Tuple,则a[i] = b对应于 PyTuple_SetItem(a,i,b),有理由相信还有一个函数PyTuple_GetItem完成取得某一项的值。
  • 不仅Python语言很优雅,Python的库函数API也非常优雅。

在Linux下面,则用gcc编译:

gcc -fPIC -shared great_module.c -o great_module.so -I/usr/include/python2.7/ -lpython2.7

在当前目录下得到great_module.so,同理可以在Python中直接使用。
我输入指令的位置在great_module.c的目录所在的位置,在该文件位置输入以下指令后,会在该目录下生成一个.so动态链接库,该库可以由python调用。


然后在该目录下用python代码调用这个库,使用该库下的函数great_function()

Python调用c++高级(swig)

这里转述的参考的文章为:利用swig对c++库进行Python包装和swig+python的用法,也查阅了swig的官方文档。

SWIG的工作方式:
SWIG本质上是个代码生成器,为C/C++程序生成到其他语言的包装代码(wrapper code),这些包装代码里会利用各语言提供的C API,将C/C++程序中的内容暴露给相应语言。为了生成这些包装代码,SWIG需要一个接口描述文件,描述将什么样的接口暴露给其他语言。SWIG的 接口描述文件可以包含以下内容:1)ANSI C函数原型声明 2)ANSI C变量声明 3) SWIG指示器(directive)相关内容。SWIG可以直接接受”.h”头文件做为接口描述文件。在有了接口描述文件后,就可以利用swig命令生 成包装代码了,然后将包装代码编译链接成可被其他语言调用的库。
SWIG对Python支持到何种程度?
利用SWIG,可以现实以下功能:1)用Python调用C/C++库;2)用Python继承C++类,并在Python中使用该继承类;3)C++使用Python扩展(通过文档描述应该可以支持,未验证)
SWIG包含的内容
一个代码生成器(swig):代码生成器根据接口说明文件,生成相应的包装代码。一个库:SWIG将常用的内容放到SWIG库里了,比如对数组、指针的支持,字符串支持,STL支持等。可以在接口文件中直接引用库里的内容,大大方便接口文件的编写。一个简单示例:

1. c库

//example.h
int fact(int n); 
//example.c
#include "example.h"  

int fact(int n) {  
    if (n < 0){ /* This should probably return an error, but this is simpler */  
        return 0;  
    }  
    if (n == 0) {  
        return 1;  
    }  
    else {  
        /* testing for overflow would be a good idea here */  
        return n * fact(n-1);  
    }  
}  

1.2. 利用swig对c库进行包装

1.2.1 写一个.i文件进行swig模块的定义

swig会运行.i文件来生成Python包的前身,然后使用编译器来将这个前身生成Python的包(.so文件),就可以import它了。

/* File: example.i */  
%module example  

%{  
#define SWIG_FILE_WITH_INIT  
#include "example.h"  
%}  

int fact(int n); 

1.2.2 通过命令行来swig来创建python包的前身

c源码的话:

swig -python example.i

这样会创建两个不同的文件:example_wrap.c 和python文件 example.py。

1.2.3 编译生成python包

进行如下的操作就可以在当前目录下编译生成python包_example.so,然后就可以直接import _example,来进行操作了。

1.2.3.1 直接使用Linux下的c++编译器

gcc -fPIC -I/usr/include/python2.7 -shared -o _example.so example_wrap.c example.c

生成的python包的名称前要加下划线,c库文件要使用gcc而不是g++(c++编译器)。

1.2.3.2 使用python.distutils生成模块动态库

使用该方法时需要配置(写)一个.py文件,然后运行该文件来生成模块动态库。
最近的python版本都会自带一个distutils工具,可以用它来创建python的扩展模块。使用它也很简单,只需要先定义一个配置文件,通常是命名为setup.py,如下

#!/usr/bin/env python  

""" 
setup.py file for SWIG example 
"""  

from distutils.core import setup, Extension  


example_module = Extension('_example',  
                           sources=['example_wrap.c', 'example.c'],  
                           )  

setup (name = 'example',  
       version = '0.1',  
       author      = "SWIG Docs",  
       description = """Simple swig example from docs""",  
       ext_modules = [example_module],  
       py_modules = ["example"],  
       )  

代码:example_module = Extension(…)创建一个扩展模块对象,并命名为_example,并使用由swig生成的example_wrap.c和用户自己的源码example.c.swig生成的扩展模块对象名必须使用python模块名并在前面加上下划线_,刚才我们通过swig生成的python文件是example.py,所以这里的模块对象名必须是’_example’,否则无法顺利编译.
然后在当前目录下(有.c、.i、.py、setup.py),运行命令:

%PYTHON_ROOT%\python setup.py build_ext –inplace

或直接

python setup.py build_ext –inplace

就可以生成_example.so了。

2. c++库

//example.h
int fact(int n); 
//example.cpp
#include "example.h"  

int fact(int n) {  
    if (n < 0){ /* This should probably return an error, but this is simpler */  
        return 0;  
    }  
    if (n == 0) {  
        return 1;  
    }  
    else {  
        /* testing for overflow would be a good idea here */  
        return n * fact(n-1);  
    }  
}  

2.2. 利用swig对c++库进行包装

2.2.1 写一个.i文件进行swig模块的定义

/* File: example.i */  
%module example  

%{  
#define SWIG_FILE_WITH_INIT  
#include "example.h"  
%}  

int fact(int n); 

2.2.2 通过命令行来swig来创建python包的前身

如果是c++源码:

swig -c++ -python example.i
这样会创建两个不同的文件:example_wrap.cxx 和python文件 example.py。

2.2.3 编译生成python包

2.2.3.1 直接使用Linux下的c++编译器

g++ -fPIC -shared example_wrap.cxx example.cpp -o _example.so -I/usr/include/python2.7/ -lpython2.7

生成的python包的名称前要加下划线,c++库文件要使用g++而不是gcc(c编译器)。

2.2.3.2 使用python.distutils生成模块动态库

使用该方法时需要配置(写)一个.py文件,然后运行该文件来生成模块动态库。
最近的python版本都会自带一个distutils工具,可以用它来创建python的扩展模块。使用它也很简单,只需要先定义一个配置文件,通常是命名为setup.py,如下

#!/usr/bin/env python  

""" 
setup.py file for SWIG example 
"""  

from distutils.core import setup, Extension  


example_module = Extension('_example',  
                           sources=['example_wrap.cxx', 'example.cpp'],  
                           )  

setup (name = 'example',  
       version = '0.1',  
       author      = "SWIG Docs",  
       description = """Simple swig example from docs""",  
       ext_modules = [example_module],  
       py_modules = ["example"],  
       )  

然后在当前目录下(有.cxx、.i、.py、setup.py),运行命令:

%PYTHON_ROOT%\python setup.py build_ext –inplace

或直接

python setup.py build_ext –inplace

就可以生成_example.so了。

3 python调用生成的_example.so

ipython
Input[0]: import example
Input[1]: example.fact(5)
Output[1]: 120

值得注意的是import的包是example而不是_example。

更多推荐

Python调用c++