从C、C++语言编写的程序中调用Python可以加快编程速度,充分利用Python编程的便捷性。

需要理解的问题:

支持callback函数的库

Callback在维基上的解释是:在计算机编程中,一个callback是一段可执行代码,它作为参数传递给其他代码,以在适当的时候使这段参数代码被调用执行(call back/execute)。它有同步callback和异步callback二种,取决于其他代码与callback代码的调用时间之间的关系。

在C语言中调用Python时,C语言代码利用callbacks来完成对Python的调用。那么,在C语言的范畴中,Python的等价描述(equivalent)中就需要为Python编程人员提供一种callback的机制;实现中,就需要在C语言的callback代码中调用Python的callback代码。Python的两个特点为上述功能的实现提供了方便:首先,Python的解释器可以被递归得调用;其次,存在调用Python函数的标准接口。

详细的调用是这样的,首先Python程序必须以某种方式提供Python函数对象,这里我们可以用一个函数或者接口来完成这个功能。当这个函数或者接口被调用的时候,在全局变量(或其他合适的域)中保存这个函数(或接口)对象的指针。下面是一个例子:

static PyObject *my_callback = NULL;

static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *temp;

    if(PyArg_ParseTuple(args, "O:set_callback", &temp))
    {
        if(!PyCallable_Check(temp))
        {
            PyErr_SetString(PyExc_TypeError, "parameter must be callable");
            return NULL;
        }
        Py_XINCREF(temp);            //为新生成callback代码对象增加一个引用
        Py_XDECREF(my_callback); //处理my_callback之前引用的对象
        my_callback = temp;          //用my_callback保存当前的callback代码引用
        // 用于返回None的样板
        Py_INCREF(Py_None);
        result = Py_None;
    }
    return result;  
}

用于提供函数对象的函数必须在解释器里注册,详细过程如下:

static PyMethodDef SetCallbackMethods[] = {
    ...
    {
        "SetCallback",        //方法的名称
        my_set_callback,   //指向C实现的指针
        METH_VARARGS,   //用于指示如何构造调用的比特标志
        "set the callback code piece object"    //指向相关文档内容的指针
    },
    ...
    {NULL, NULL, 0, NULL}   //标记
};

 上面代码中的PyMethodDef是一个结构体(方法表),用于描述Python中扩展类型的方法。主要有4个域

 


C类型

含义

ml_name

char *

方法的名称

ml_method

PyCFunction

指向C实现的指针

ml_flags

int

用于指示如何构造调用的比特标志

ml_doc

char *

指向相关文档内容的指针

其中,ml_method是一个C函数指针,它所指向的函数全部返回PyObject * 类型的引用(新引用,在Python中可见)。PyCFunction是C语言中用于实现可调用Python模块的典型函数类型。这类函数有2个PyObject*类型的参数(需要理解Python中self的C类型表示)。ml_flags包含calling或者binding两种协议,如果是0则表示使用PyArg_ParseTuple()变量(已过时)。PyArg_ParseTuple(PyObject *args, const char *format, ...)用于解析一个函数的参数,并按照位置把参数保存到本地变量中。如果ml_flags是METH_VARARGS,则函数只应该接收Python级(由PyArg_ParseTuple()函数来解析)的参数;如果参数接收关键字字典,则METH_KEYWORDS比特位应该置位(METH_VARARGS|METH_KEYWORDS),对应的函数应该有第三个PyOjbect*类型的参数用于接收关键字字典(由PyArg_ParseTupleAndKeywords()来解析)。

在Python模块初始化阶段,符号表必须要给到解释器。初始化函数要命名为initname(),其中name是模块的名称,这个函数是模块文件中唯一的非静态成员。例如,

PyMODINIT_FUNC
initSetCallback(void)
{
    (extern "C") Py_InitModule("SetCallback", SetCallbackMethods);
}

当Python程序第一次imports “SetCallback”模块时,initSetCallback被调用。Py_InitModule()生成一个新的module对象,并在sys.modules 字典中插入以SetCallback为key的元素,根据Py_InitModule的第二个参数(PyMethodDef型数组),将内建的函数对象插入到新模块中,如果成功,返回指向这个新模块的指针。

当在C或C++代码中嵌入Python代码时,initSetCallback如果在_PyImport_Inittab表中有记录,就会被自动调用;否则,需要在代码中直接调用,即initSetCallback(),例如:

int main(int argc, char *argv[])
{
    // 向解释器传递第一个参数
    Py_SetProgramName(argv[0]);
    // 初始化一个Python解释器,这个是必需的
    Py_Initialize();
    // 增加一个静态的模块
    initSetCallback();
    ......
}

关于PyArg_ParseTuple(PyObject *arg, char *format, ...),arg 是一个包含参数列表的tuple对象,由Python传递到C函数里,format字符串由0到多个格式单元组成,一个格式单元描述一个Python对象,多个格式单元用括号包围,逗号分隔。