在之前的博客中我实践在C++中调用Python函数,即C++能够“知道”Python定义的东西。而本篇将实践让Python能够“知道”C++定义的东西,即:将C++函数暴露给Python。
我从《如何实现 C/C++ 与 Python 的通信? - 知乎》中获得了些提示。
不过代码主要参考了《1. 使用 C 或 C++ 扩展 Python — Python 3.9.1 文档》。
(执行python文件方面从《【Python有坑系列】报错NameError: name ‘execfile’ is not defined_小白兔de窝-CSDN博客》获得了提示)
首先,我的C++函数定义如下:
int MyCppFunction(int x)
{
return x * 2 + 3;
}
随后,将其封装为Python的格式:
static PyObject * MyCppFunction_python(PyObject *self, PyObject *args)
{
//输入的参数
int parm;
//将python参数以int为格式解析,写入parm
if (!PyArg_ParseTuple(args, "i", &parm))
return NULL;
//调用原生的C++函数
int result = MyCppFunction(parm);
//将结果转变为Python格式
return PyLong_FromLong(result);
}
然后,定义一个表,将上述的函数放入其中:
static PyMethodDef Methods[] =
{
{
"MyCppFunction", //函数名字
MyCppFunction_python, //封装为Python格式的C++函数
METH_VARARGS, // Combination of METH_xxx flags, which mostly describe the args expected by the C func
NULL, // The __doc__ attribute, or NULL
},
{
NULL, NULL, 0, NULL //全是0的元素,表中最后的元素一定要是它,扮演一个“哨兵”的角色来标志表何时结束
},
};
注意结尾应是{NULL, NULL, 0, NULL}
(感觉就像是字符串的结尾是\0
作为标志)。
然后定义一个叫yaksue
的模块,并填入上表
static struct PyModuleDef yaksue_module =
{
PyModuleDef_HEAD_INIT,
"yaksue", //模块名字
NULL, //模块文档,可以是NULL
-1, // size of per-interpreter state of the module, or -1 if the module keeps state in global variables.
Methods //方法表
};
随后,定义一个用来初始化这个模块的函数:
PyMODINIT_FUNC
PyInit_yaksue_module(void)
{
return PyModule_Create(&yaksue_module);
}
下面,写一个Python脚本(D:/Temp/script.py),调用函数:
# 导入模块
import yaksue
# 测试打印出模块
print(yaksue)
#测试打印函数定义
print(yaksue.MyCppFunction)
# 测试调用C++函数
print(yaksue.MyCppFunction(6))
然后,在C++的main函数中,创建yaksue
这个内建模块,然后执行 D:/Temp/script.py 这个文件:
int main()
{
//添加一个内建模块
//这一步需要放在Py_Initialize之前
if (PyImport_AppendInittab("yaksue", PyInit_yaksue_module) == -1)
{
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
//设定Python解释器的名字
Py_SetProgramName(L"YaksueTestPython");
//初始化Python解释器。
//这一步是必须的,如果出错,则会有一个 fatal error
Py_Initialize();
//执行 D:/Temp/script.py
PyRun_SimpleString("exec(open('D:/Temp/script.py', encoding = 'utf-8').read())");
Py_Finalize();
return 0;
}
C++:
#include
#include
//我的C++函数:
int MyCppFunction(int x)
{
return x * 2 + 3;
}
//将我的C++函数封装成Python的格式
static PyObject * MyCppFunction_python(PyObject *self, PyObject *args)
{
//输入的参数
int parm;
//将python参数以int为格式解析,写入parm
if (!PyArg_ParseTuple(args, "i", &parm))
return NULL;
//调用原生的C++函数
int result = MyCppFunction(parm);
//将结果转变为Python格式
return PyLong_FromLong(result);
}
//记录所有方法的表
static PyMethodDef Methods[] =
{
{
"MyCppFunction", //函数名字
MyCppFunction_python, //封装为Python格式的C++函数
METH_VARARGS, // Combination of METH_xxx flags, which mostly describe the args expected by the C func
NULL, // The __doc__ attribute, or NULL
},
{
NULL, NULL, 0, NULL //全是0的元素,表中最后的元素一定要是它,扮演一个“哨兵”的角色来标志表何时结束
},
};
//yaksue模块定义
static struct PyModuleDef yaksue_module =
{
PyModuleDef_HEAD_INIT,
"yaksue", //模块名字
NULL, //模块文档,可以是NULL
-1, // size of per-interpreter state of the module, or -1 if the module keeps state in global variables.
Methods //方法表
};
PyMODINIT_FUNC
PyInit_yaksue_module(void)
{
return PyModule_Create(&yaksue_module);
}
int main()
{
//添加一个内建模块
//这一步需要放在Py_Initialize之前
if (PyImport_AppendInittab("yaksue", PyInit_yaksue_module) == -1)
{
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
//设定Python解释器的名字
Py_SetProgramName(L"YaksueTestPython");
//初始化Python解释器。
//这一步是必须的,如果出错,则会有一个 fatal error
Py_Initialize();
//执行 D:/Temp/script.py
PyRun_SimpleString("exec(open('D:/Temp/script.py', encoding = 'utf-8').read())");
Py_Finalize();
return 0;
}
Python:
# 导入模块
import yaksue
# 测试打印出模块
print(yaksue)
#测试打印函数定义
print(yaksue.MyCppFunction)
# 测试调用C++函数
print(yaksue.MyCppFunction(6))
.pyd
)作为模块,待试验。