您的位置:首页 > 编程语言 > C语言/C++

使用swig实现C++的python扩展功能

2014-11-15 09:11 951 查看
1. 直接C扩展Python的方法

 Python的扩展API是主要为C语言提供的,可以利用该API为要导出的C函数建立包装器(wrapper)。包装器用来处理Python对象与底层C函数中的变量所需值之间的转换,并将C函数注册到Python的自定义module中。

 假设将要导出的函数成为模块函数,对应wrapper中的函数为包装函数。则wrapper文件主要包含以下几个部分:

include "Python.h";

每个模块函数的包装函数,即:PyObject* Module_func();

模块函数与包装函数的映射表,即:PyMethodDefModuleMethods[]对应表;

模块的初始化函数:void initModule()部分。

  简单示例程序:

  假设有以下C文件TestModel.c:

int add(int x,int y)

{

    returnx+y;

}

 我们要导出其中的函数add(),则要为之写wrapper。文件内容如下:

include "Python.h";

extern int add(int);

PyObject *add_wrap(PyObject *self, PyObject *args)

{

    int x=0;

    inty=0;

    intresult=0; 

    if (!PyArg_ParseTuple(args, "i|i", &x, &y))

    returnNULL;

    result =add(x,y)

    returnPy_BuildValue("i", result);

}

参数说明:

   第一个参数是self,这个是python用的,每个函数都要有。我们暂时不管。  

   第二个参数是args,它是一个参数列表。她把所有的参数都整合成一个string。所以我们需要从这个string里来解析我们的参数。我们使用python内置函数PyArg_ParseTuple来完成这个任务。其中,第一个参数是args,就是我们要转换的参数;第二个是格式符号。“s”代表是个string。从args里提取一个参数就写"s",两个的话就写"s|s", 如果是一个string,一个int,就写"s|i",和printf差不多;第三个参数就是提取出来的参数放置的真正位置。必须传递这个参数的地址。对于add,他将提取两个参数。分别是x和y。

 

调用完add后,我们需要返回结果。这个结果是c的type或者是我们自己定义的类型。必须把它转换成PyObject,让python认识。这个用Py_BuildValue来完成。它是PyArg_ParseTuple的逆过程。他的第一个参数和PyArg_ParseTuple的第二个参数一样,是个格式化符号。第二个参数是我们需要转换的参数。Py_BuildValue会把所有的返回值组装成一个tutple给python。

static PyMethodDef TestModelMethods[] =

{

  {"add", add_wrap, METH_VARARGS, "add two numberto one number"},

  {NULL, NULL, 0, NULL}

};

 

  这个是一个c的结构。它来完成一个映射。我们需要把我们扩展的函数都映射到这个表里。表结构说明如下:

    第一个字段是python真正认识的。是python里的方法名字。

    第二个字段是python里的这个方法名字的具体实现的函数名。在python里调用add,真正执行的是用c写的add_wrap函数。

    第三个字段是METH_VARARGS,他告诉python,add_wrap是调用c函数来实现的。

    第四个字段是这个函数的说明。如果你在python里来help这个函数,将显示这个说明。相当于在python里的函数的文档说明。

void initTestModel(void)

{

    Py_InitModule("TestModel",TestModelMethods);

}

  注意,这个函数的名字不能改动。必须是init+模块名字。我们的模块名字是TestModel。所以这个函数是initTestModel()。

这样python在导入TestModel的模块时候,才会找到initTestModel()这个函数,并调用。这个函数调用Py_InitModule来将模块名字和映射表结合在一起。

他表示,TestModel这个模块使用TestModelMethods这个映射表。python应该这样导入我们的module的.然后我们可以将生成的模块导入到Python环境中,并调用其中的函数。

 

2. 怎样用SWIG生成C的wraper

 SWIG是一个自动的扩展构造工具。它读入注释的源程序头文件(后缀为.i的脚本文件),为python、tcl、perl等多种脚本语言产生wrap代码。

  步骤大致为:

(1) 写源程序;

(2) 写后缀为.i的脚本文件;

(3) 使用命令"swig -pythonTestModel.i"生成TestModel_wrap.c,example_wrap.doc;

(4) 编译连接成共享库。

 .i脚本文件的格式很简单,只需要列出要导出的函数或类型,并注释必要信息。还以上面的TestModel.c为例,写TestModel.i脚本文件如下:

%module TestModel

%{

#include "TestModel.h"

%}

extern int add(int);

  在命令行输入以下命令"swig -pythonTestModel.i",会在当前目录下生成TestModel_wrap.c,TestModel_wrap.doc两个文件。前者即为TestModel.c的wrap文件,后者为解释文件。

 接下来使用编译连接命令生成TestModel.so文件。命令同第一节:

 接下来就可以在Python环境中使用TestModel模块中的函数了。

3. 用SWIG生成C++的wraper

  用SWIG生成C++的wrapper过程类似于Cwrapper。它的步骤如下:

(1) 写.cpp源程序;

(2) 写.i脚本文件;

(3) swig -c++ -python foo.i

生成TestModel_wrap.c,TestModel_wrap.doc;

(4) 编译连接成共享库TestModel.so。

则生成Python的foo模块,可以通过调入foo模块使用其中的类成员函数、成员变量。

  一个简单示例:

  假设有如下C++源程序:

class TestModel

{

public:

   TestModel();

    void add(intvalue);

 };

  对应实现的cpp文件如下:

#include "TestModel.h"

TestModel::TestModel()

{

   printf("测试模块");

}

int TestModel::add(int x,int y)

{

    return x+y;

}

为之写TestModel.i文件:

%module TestModel

%{

#include "TestModel.h"

%}

class TestModel

{

public:

   TestModel();

    intadd(int x,int y);

};

编译方式:

(1.控制台方式)

 在控制台里执行使用命令:"swig -c++ -pythonTestModel.i",得到TestModel_wrap.c,TestModel_wrap.doc(有些系统生成的是number.py)两个文件。  

 生成TestModel.so文件,可以在Python环境中使用TestModel类及其成员函数。但类名和成员函数名稍有变化,具体可见生成的TestModel_wrap.doc(或number.py)文件。

(2.编辑器方式)

在.net编辑器里选中.i文件,然后在属性里进行设置:

命令行:

..\SDK\swigwin\swig  -c++-python -outdir $(ConfigurationName) -I../.."$(InputPath)"

输出:$(InputDir)$(InputName)_wrap.cxx

并且在工程属性里要指定python库的相关文件,包括include和libs。

最后,进行编译得到*.cxx文件,然后将该文件添加到工程重新进行编译。

  也可以在C++里执行,如下:

#include <iostream>

#include <string>

#include "TestModel_wrap.cxx"

int main() {

   Py_Initialize();               // initialize python 

   init_Test();  //在test_wrap.cxx里定义的。

   PyRun_SimpleString("import testpy\n

                       a = TestModel.TestModel()\n

                       b =a.add(1,2)\n

                       print b\n");

   Py_Finalize();                 // shut down python

    return0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: