從C、C++中調用Python


從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對象,多個格式單元用括號包圍,逗號分隔。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM