Python调用C/C++里的函数

Python调用C/C++里的函数

Python调用C语言的函数主要分为三部分:标准的C语言函数库、C语言Python调用模块(wrapper)、和标准的Python调用。

标准C语言函数库

里面有一系列的C语言函数。

1
2
3
4
int add(int m, int n)
{
return m + n;
}

calculate.c

标准Python调用

1
2
3
$ python
> import calculate
> calculate.add(3, 4)

C语言Python调用模块

C语言Python wrapper,python原生代码可以通过这个wrapper来调用C语言函数文件里的函数。

这个模块分为三部分:导出函数、方法列表和初始化函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* Exported function */
static PyCal_add(PyObject *self, PyObject *args)
{
/* Define variables */
int m, n;
int ret;
PyObject *obj;

/* Parse Python args to C args */
PyArg_ParseTuple(args, "ii", &m, &n);

/* Call C native function */
ret = add(m, n);

/* Convert C variable to Python */
obj = Py_BuildValue("i", ret);

/* Return (converted)Python Object */
return obj;
}

/* Method list */
static PyMethodDef CalMethods[] = {
{"add", PyCal_add, METH_VARARGS, "Add two integer values."},
{NULL, NULL, 0, NULL}
};

/* Initialization function */
PyMODINIT_FUNC
initcalcalate(void)
{
PyObject *m;
m = Py_InitModule("calculate", CalMethods);
if(m == NULL) {
return;
}
}

导出函数

连接C语言函数和Python函数的中间件,由它来进行相互转换。把Python的参数转成C的参数传递过去,然后再把C的输出转换成Python格式返回。

  • 转换Python参数为C语言类型

该函数的第二个参数args就是原生Python调用传递过来的参数,是一个C的Python Tuple Object,需要转换成标准的C语言数据类型。简单的参数可以使用PyArg_ParseTuple函数来进行解析,其中第二个参数是C语言中的类型表示符,示例中的意思是这个Python传过来的参数包含两个整型(int)数值并且分别赋值给m和n。

复杂点的参数就需要用PyTuple_GetItem进行解析了,比如args分别包含了一个int型和一个字典类型数据就需要如下操作:

1
2
3
4
5
6
7
8
int m;
PyObject *dict = NULL;
PyObject int_m = NULL; /* int_m is a PyInt_Type */

int_m = PyTuple_GetItem(args, 0);
m = PyInt_AsLong(int_m);

dict = PyTuple_GetItem(args, 1);

此处的PyObject dict又可以使用PyDict_GetItemString(dict, key)来解析,如果这个dict的key不是字符串,可以使用PyDict_GetItem(dict, PyObjectKey)。

  • 转换C语言数据为Python类型并返回

简单的C语言数据可以使用Py_BuildValue来转换,示例中obj = Py_BuildValue("i", ret)意思就是将ret转换成Python的int Object。

复杂的(比如返回值类型是list)可以用PyList_SetItem来转换,如这个返回的list中第一个值是字符串第二个是dict:

1
2
3
4
5
6
7
8
9
PyObject *list = NULL;
PyObject *dict = NULL;

PyDict_SetItemString(dict, 'key', PyObjectValue);
PyList_SetItem(list, 0, PyObjectStringValue); /* use PyString_FromString to
convert c string to python String */
PyList_SetItem(list, 1, dict);

return list;

calculate_module.c

在ParseTuple和Py_BuildValue中可以使用的C类型

Python的类型怎样和C的映射

方法列表和初始化函数

你C函数库中所有函数都需要写到这里面,如下:

1
2
3
4
5
6
static PyMethodDef CalMethods[] = {
{"add", PyCal_add, METH_VARARGS, "Add two integer values."},
{"square", PyCal_square, METH_VARARGS, "Square one integer value."},
/* ... */
{NULL, NULL, 0, NULL}
};

参考

编译链接

1
2
3
4
5
6
$ gcc -fpic -c -I /usr/include/python2.7 \
-I /usr/lib/python2.7/config-x86_64-linux-gnu calculate_module.c
$ gcc -shared -o calculate.so calculate_module.o
$ python
> import calculate
> calculate.add(3, 4)

gdb默认优化了编译选项,这会导致你调试时出现行号不对的问题,这时你需要在gcc编译时加上-O0来禁用优化来保证调试时行号与源文件保持一致。

gdb调试python脚本及c module

1
2
3
4
5
6
7
8
# gdb python
(gdb) b PyCal_add
(gdb) dir /path/to/caculate_module_dir
(gdb) run test.py
(gdb) l
(gdb) n
(gdb) n
(gdb) p xx

参考链接

  1. http://www.ibm.com/developerworks/cn/linux/l-pythc/
  2. https://docs.python.org/2.7/c-api/index.html
  3. https://docs.python.org/2.7/extending/index.html