Python 以簡潔語法、豐富標準庫、跨平台特性成為理想腳本語言。本文詳細講解如何在 C++ 應用中**嵌入 Python 解釋器**(調用 Python 函數、獲取變量)與**編寫 C++ 擴展模塊**(供 Python 調用 C++ 功能),實現雙向無縫互操作。
C++ 應用通過插件機制實現功能擴展,傳統方案使用動態鏈接庫(DLL),修改需重新編譯鏈接。改用 Python 腳本作為插件,可熱加載、即時修改;但部分底層操作(硬件訪問、高性能計算)仍需 C++ 實現,再暴露給 Python 調用。因此需要雙向橋接:
import 後的 Python 代碼調用。| API | 作用 | 示例 |
|---|---|---|
Py_Initialize() | 初始化解釋器,必須最先調用 | Py_Initialize(); |
PyRun_SimpleString() | 執行一段 Python 代碼字符串 | PyRun_SimpleString("print('Hello')"); |
PyImport_ImportModule() | 導入 .py 模塊,返回模塊對象 | pmod = PyImport_ImportModule("mymod"); |
PyModule_GetDict() | 獲取模塊的 __dict__(命名空間字典) | pdict = PyModule_GetDict(pmod); |
PyDict_GetItemString() | 從字典取出函數/變量對象 | pfunc = PyDict_GetItemString(pdict, "myfunc"); |
PyObject_CallObject() | 調用可調用對象(函數/類) | presult = PyObject_CallObject(pfunc, pargs); |
PyLong_AsLong() | 將 Python 整數轉為 C long | long val = PyLong_AsLong(presult); |
Py_Finalize() | 關閉解釋器,釋放資源 | Py_Finalize(); |
#include <Python.h>
#include <iostream>
int main() {
Py_Initialize(); // 1. 初始化
// 2. 執行簡單腳本
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./scripts')");
// 3. 導入自定義模塊
PyObject* pModule = PyImport_ImportModule("business_logic");
if (!pModule) { PyErr_Print(); return -1; }
// 4. 獲取模塊字典與目標函數
PyObject* pDict = PyModule_GetDict(pModule);
PyObject* pFunc = PyDict_GetItemString(pDict, "process_data");
if (!pFunc || !PyCallable_Check(pFunc)) { /* 錯誤處理 */ }
// 5. 構造參數元組 (int, string)
PyObject* pArgs = PyTuple_New(2);
PyTuple_SetItem(pArgs, 0, PyLong_FromLong(42));
PyTuple_SetItem(pArgs, 1, PyUnicode_FromString("test"));
// 6. 調用並獲取返回值
PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
long result = PyLong_AsLong(pResult);
std::cout << "Python returned: " << result << std::endl;
// 7. 清理
Py_DECREF(pArgs); Py_DECREF(pModule); Py_DECREF(pResult);
Py_Finalize();
return 0;
}
核心步驟:定義靜態 PyMethodDef 陣列描述導出函數 → 實現 C 函數(簽名固定為 PyObject* func(PyObject*, PyObject*)) → 定義模塊定義結構 PyModuleDef → 實現 PyInit_modname() 入口函數。
// myext.cpp
#include <Python.h>
static PyObject* myext_add(PyObject* self, PyObject* args) {
long a, b;
if (!PyArg_ParseTuple(args, "ll", &a, &b)) return NULL;
return PyLong_FromLong(a + b);
}
static PyMethodDef MyExtMethods[] = {
{"add", myext_add, METH_VARARGS, "Add two integers."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef myextmodule = {
PyModuleDef_HEAD_INIT, "myext", NULL, -1, MyExtMethods
};
PyMODINIT_FUNC PyInit_myext(void) {
return PyModule_Create(&myextmodule);
}
編譯為 myext.pyd (Windows) 或 myext.so (Linux),放入 Python 路徑,即可 import myext; print(myext.add(3,5))。
Py_INCREF/Py_DECREF,防止內存洩漏或崩潰;PyErr_Occurred(),Python 端捕獲 C++ 拋出的異常需編寫包裝層;PyGILState_Ensure/Release);掌握 C/C++ 與 Python 的雙向互調,能將 C++ 的高性能與 Python 的靈活開發效率完美結合,是構建可擴展、可腳本化高性能系統的關鍵技能。