Python调用c++
Python与c++通信,作者描述了Python与c++相互通信的方法,依据文章所述,做了相关的实验,在此转述作者文章并记录自己的学习过程。
1. Python调用c++(基础篇)
2. Python调用c++(高级篇,使用swig工具)
Python调用c++
作者:Jerry Jho
链接:https://www.zhihu.com/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。