欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python调用c++

程序员文章站 2022-05-09 15:06:20
...

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调用c++
Python调用c++
然后在该目录下用python代码调用这个库,使用该库下的函数great_function()
Python调用c++

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。

相关标签: p