Python 扩展 C++

参考文章:
https://www.zouyesheng.com/python-module-c.html    


整体流程

  1. 处理从Python中传入的参数
  2. 使用C/C++实现功能逻辑
  3. 将功能逻辑产生的返回值包装成Python所需的格式
  4. 注册函数
  5. 注册模块名
  6. 编译

具体步骤

一、引入头文件

#include <Python.h>

该文件一般位于python安装目录的include文件夹下。

二、处理从Python中传入的参数   

图片摘自:https://blog.csdn.net/taiyang1987912/article/details/44779719

// python list -> c++ vector
void pyListToPointVec(PyObject* object, vector<Point>& contour)
{
    if(!PyList_Check(object))
        return;

    int point_num = PyList_Size(object);

    contour.clear();
    for(int i = 0; i< point_num; i++){
        PyObject* rowArr = PyList_GetItem(object,i);

        PyObject* py_x = PyList_GetItem(rowArr, 0);
        PyObject* py_y = PyList_GetItem(rowArr, 1);

        int x = PyInt_AsLong(py_x);
        int y = PyInt_AsLong(py_y);
        contour.push_back(Point(x, y));
    }
}

// python tuple -> c++ parameters
void pyParamToCppType(PyObject* args, int* param1, int* param2)
{
    // i|i 表示将参数解析为两个int,同理,s表示字符串
    PyArg_ParseTuple(args, "i|i", &param1, &param2);
}

三、实现功能逻辑

即希望借助C++进行扩展的功能模块,例如:

int my_add(int x, int y)
{
    return x + y;
}    

四、处理C/C++的返回值

// c++ vector -> python list
PyObject* pointVecToPyList(vector<Point>& contour)
{
    int sz = contour.size();
    PyObject* res_cnt = PyList_New(sz);
    for(int idx1 = 0; idx1 < sz; ++idx1)
    {
        PyObject* onePoint = PyList_New(2);;

        int x = contour[idx1].x;
        int y = contour[idx1].y;

        PyObject* py_x = PyInt_FromLong(x);
        PyObject* py_y = PyInt_FromLong(y);

        PyList_SetItem(onePoint, 0, py_x);
        PyList_SetItem(onePoint, 1, py_y);

        PyList_SetItem(res_cnt, idx1, onePoint);
    }

    return res_cnt;
}


// c++ int -> python int
PyObject* cppIntToPyInt(int res)
{
    // i 表示int类型,同理,s表示字符串
    return Py_BuildValue("i", res);
}

五、编写包装函数    

即对上述三部分进行包装,函数声明形式一般如下所示,且一般应声明为static类型。函数的第一个参数为默认传入的Python对象,第二个参数为所需参数列表(实际上已被序列化成一个字符串)

// 包装函数
static PyObject* alg(PyObject *self, PyObject *args)
{
    // get input parameters [[], [], …]
    PyObject* arg_list = PyTuple_GetItem(args, 0);
    
    // python list -> c++ vector
    vector<Point> contour;
    pyListToPointVec(arg_list, contour);

    // 真正要实现的算法
    Alg(contour);
    
    // c++ vector -> python list
    PyObject* result = pointVecToPyList(contour);

    return result;
}


static PyObject* myPyAdd(PyObject *self, PyObject *args)
{
    int x, y;
    PyArg_ParseTuple(args, "i|i", &x, &y);
    
    return Py_BuildValue("i", x + y);
}

六、注册函数

导出工作的第一步:在类型为PyMethodDef的结构体中注册需要导出的函数。

static PyMethodDef ExtendMethods[] ={
        {"alg", alg, METH_VARARGS, "algorithm from cpp"},
        {NULL, NULL}
};

几点说明:   

  • "alg" 为导出到Python中可见的方法名
  • alg 为C/C++ 中待导出的函数名
  • METH_VARARGS 表示传入函数的是普通参数,还有关键词参数等(未试验)

七、注册模块 

 导出工作的第二步:定义一个命名为initXXX的函数,XXX必须是模块名。

PyMODINIT_FUNC initdemo(void)
{
    Py_InitModule("demo", ExtendMethods);
}

几点说明:

  • 调用Py_InitModule注册模块
  • 第一个参数为导出模块的模块名
  • 第二个参数为上一步中定义的PyMethodDef结构体的名称

八、编译

使用GCC编译生成动态链接库,例如:

gcc main.cpp -I /usr/local/include/python2.7 -shared -fPIC -o demo.so

九、调用

# test.py

import demo
res = demo.alg(xxx)


 

猜你喜欢

转载自blog.csdn.net/sinat_37532065/article/details/86537247