Python/C Integration
Python语言被设计成可以和C/C++、Java、C#等编程语言混合编程。Python解释器就是使用C/C++语言编写的,所以能够和这些语言混合编程并不奇怪,只需要在设计Python解释器时留好接口即可,实际上Python之所以被称为”脚本”语言,就是因为它可以和其他语言进行混合编程,Python只需要处理好上层的”脚本”逻辑,将具体底层逻辑的控制权交给更擅长的其他编程语言处理。
Python和C/C++混合编程分为两个方面:
- Python调用C/C++,在本文称之为Extending Python with C/C++;
- C/C++调用Python,在本文称之为Embedding Python in C/C++;
本文重点介绍前者,后者有空补充。
Ⅰ. Extending Python with C/C++
在Python中调用C/C++代码可分为两大部分内容:
- C Extension Module: 定义函数
- C Extension Type: 定义类
1. C Extension Module
1.1 All by Hand
Python解释器本身由C/C++编写,它留有一些C/C++接口使得用户可编写指定格式的C/C++程序并以.so动态库的形式与Python解释器交互。
Python解释器与.so动态库的交互方式非常简单,直接import然后把它当做是普通的Python Module即可。例如hello.so,可以直接import hello即可使用。
我们下面看一个例子:
/********************************************************************
* hello.c
* A simple C extension module for Python, called "hello"; compile
* this into a ".so" on python path, import and call hello.message;
********************************************************************/
#include <Python.h>
#include <string.h>
/* module functions */
static PyObject *message(PyObject *self, PyObject *args) {
char *fromPython, result[1024];
// convert Python -> C
if (!PyArg_Parse(args, "(s)", &fromPython)) {
return NULL; // null = raise exception
} else {
strcpy(result, "Hello, ");
strcat(result, fromPython);
// convert C -> Python
return Py_BuildValue("s", result);
}
}
/* registration table */
static PyMethodDef hello_methods[] = {
{"message", message, METH_VARARGS, "func doc"}, // name, &func, fmt, doc
{NULL, NULL, 0, NULL} // end of table marker
};
/* module definition structure */
static struct PyModuleDef hellomodule = {
PyModuleDef_HEAD_INIT, "hello", // name of module
"mod doc", // module documentation, may be NULL
-1, // size of per-interpreter module state, -1 = in global vars
hello_methods // link to methods table
};
/* module initializer */
PyMODINIT_FUNC PyInit_hello() { return PyModule_Create(&hellomodule); }
将其编译为.so动态库:
export PY_INCLUDE=/usr/include/python3.6
gcc hello.c -I${PY_INCLUDE} -fPIC -shared -o hello.so
之后将hello.so当做是普通的Python Module使用:
>>> import hello
>>> hello.message("World")
'Hello, World'
>>> dir(hello)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'message']
这个例子的重点是使用Python预留的C/C++接口将C/C++程序编译成Python解释器可以import的.so动态库。这种方法最接近本质,但操作起来比较麻烦,需要用户记住很多Python预留的API且要按照指定格式去编写C/C++代码。
下面我们使用SWIG工具自动生成上述C/C++代码。
1.2 Use SWIG
/********************************************************************
* hellolib.h
* Define hellolib.c exports to the C namespace, not to Python
* programs--the latter is defined by a method registration
* table in a Python extension module's code, not by this .h;
********************************************************************/
extern char *message(char *label);
/*********************************************************************
* hellolib.c
* A simple C library file, with a single function, "message",
* which is to be made available for use in Python programs.
* There is nothing about Python here--this C function can be
* called from a C program, as well as Python (with glue code).
*********************************************************************/
#include <string.h>
#include <hellolib.h>
static char result[1024];
char *message(char *label) {
strcpy(result, "Hello, ");
strcat(result, label);
return result;
}
/******************************************************
* hellolib.i
* Swig module description file, for a C lib file.
* Generate by saying "swig -python hellolib.i".
******************************************************/
%module hellowrap
%{
#include <hellolib.h>
%}
extern char *message(char*); /* or: %include "../HelloLib/hellolib.h" */
/* or: %include hellolib.h, and use -I arg */
首先使用Swig生成wrapper文件:
swig -python hello.i
这会生成两个新文件:hello.py和hello_wrap.c
然后编译:
gcc hello.c hello_wrap.c -I${PY_INCLUDE} -I. -fPIC -shared -o _hello.so
使用,直接import hello即可,hello.py回链接_hello.so:
>>> import hello
>>> hello.message("World")
'Hello, World'
2. C Extension Type
2.1 All by hand
略
2.2 Use SWIG
// number.h
class Number {
public:
Number(int start); // constructor
~Number(); // destructor
void add(int value); // update data member
void sub(int value);
int square(); // return a value
void display(); // print data member
int data;
};
// number.cpp
#include "number.h"
#include "stdio.h"
Number::Number(int num) {
data = num; // python print goes to stdout
printf("Number: %d\n", num); // or: cout << "Number: " << data << endl;
}
Number::~Number() { printf("~Number: %d\n", data); }
void Number::add(int value) {
data += value;
printf("add %d\n", value);
}
void Number::sub(int value) {
data -= value;
printf("sub %d\n", value);
}
int Number::square() {
return data * data; // if print label, fflush(stdout) or cout << flush
}
void Number::display() { printf("Number=%d\n", data); }
/********************************************************
* number.i
* Swig module description file for wrapping a C++ class.
* Generate by running "swig -c++ -python number.i".
* The C++ module is generated in file number_wrap.cxx;
* module 'number' refers to the number.py shadow class.
********************************************************/
%module number
%{
#include "number.h"
%}
%include number.h
编译与使用过程与C Extending Module移植。
注意,SWIG是接口协议,无需关心具体实现。
Ⅱ. Embedding Python in C/C++
TODO