Python访问C程序

74 阅读2分钟

Python访问C程序

最近碰到了Python调用C++代码的需求,c++代码需要先导出C接口才能给Python调用,本质上解决python调用C代码就行了

假如我需要导出我的这两个C函数

int fab(int n)
{
    if (n == 1 || n == 0)
        return 1;
    else
        return n * fab(n - 1);
}

void quick_sort(int arr[], int left, int right)
{
    int i, j, temp, pivot;
    if (left < right)
    {
        i = left;
        j = right;
        pivot = arr[(left + right) / 2];

        while (i <= j)
        {
            while (arr[i] < pivot)
            {
                i++;
            }
            while (arr[j] > pivot)
            {
                j--;
            }
            if (i <= j)
            {
                temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i++;
                j--;
            }
        }

        quick_sort(arr, left, j);
        quick_sort(arr, i, right);
    }
}

需要如下一些步骤

  1. 导出Python.h头文件,这个文件需要安装python3-env动态库
  2. 定义模块
  3. 模块中配置要导出的接口
  4. 在接口中将python类型解析成C类型
  5. 编译成so
  6. python中引入模块,调用接口

定义了模块的init函数以及模块的名字和usage信息

static struct PyModuleDef moduleExport = {
    PyModuleDef_HEAD_INIT,
    "moduleExport",
    "usage",
    -1,
    exportFunctions};

PyMODINIT_FUNC PyInit_moduleExport(void)
{
    return PyModule_Create(&moduleExport);
}

定义模块导出函数,对于每个要导出的函数要指定在python中符号的名字

static PyMethodDef exportFunctions[] =
    {
        {"fab", wrap_fab, METH_VARARGS, "Caculate N!"},
        {"sort", wrap_quick_sort, METH_VARARGS, "排序!"},
        {NULL, NULL}};

类型转换

PyObject *wrap_fab(PyObject *self, PyObject *args)
{
    int n, result;

    if (!PyArg_ParseTuple(args, "i:fab", &n))
        return NULL;
    result = fab(n);
    return Py_BuildValue("i", result);
}

从arg中解析得到list,申请一块内存,将list中的数据拷贝到内存中,完毕后创建一个新list,将结果拷贝到新的list中并返回,这里如果直接返回旧的list会segment fault。

PyObject *wrap_quick_sort(PyObject *self, PyObject *args)
{
    PyObject *my_list;
    if (!PyArg_ParseTuple(args, "O", &my_list))
    {
        return NULL;
    }
    if (!PyList_Check(my_list))
    {
        PyErr_SetString(PyExc_TypeError, "Expected a list");
        return NULL;
    }
    int list_size = PyList_Size(my_list);
    int *my_array = malloc(sizeof(int) * list_size);
    for (int i = 0; i < list_size; i++)
    {
        PyObject *item = PyList_GetItem(my_list, i);
        if (!PyLong_Check(item))
        {
            PyErr_SetString(PyExc_TypeError, "List must contain integers");
            return NULL;
        }
        my_array[i] = PyLong_AsLong(item);
    }
    quick_sort(my_array, 0, list_size - 1);

    my_list = PyList_New(list_size);
    for (int i = 0; i < list_size; i++)
    {
        PyList_SetItem(my_list, i, Py_BuildValue("i", my_array[i]));
    }
    free(my_array);

    return my_list;
}

使用这个命令编译,动态库的名字得和你的模块名匹配,这里有三个名字要匹配,一个是模块名,一个是动态库名,最后是PyInit_xxxx

gcc -fPIC hello.c -o moduleExport.so -shared  -I/usr/include/python3.8 

python中直接调用

import moduleExport
print(moduleExport.fab(10))

res = moduleExport.sort([1,3,2])
print(res)