一:环境配置
注:我使用的环境:IDLE (Python 3.6 32-bit) ;vs2010
以下环境配置部分详见:混合编程之——C++调用python2.7&python3.5
以python3.6为例:
需要单独将Python36-32下面的,include文件夹里面的头文件和libs文件夹里面的库函数都单独拷贝出来,至于怎么设置,怎么放,咱们慢慢来。
1、新建一个文件夹,取名为test_python36;
2、将Python36-32下面的,include文件夹、libs文件夹都拷贝到test_python36文件夹中,并把libs文件夹中的python36.lib拷贝一份并命名为python36_d.lib放在该文件下(libs下);
3、打开VS2010,新建一个项目(C_python),选好路径,文件夹test_python36里面的内容如下:
4、接下来需要配置项目(C_python)的环境变量
右键项目-》属性
(1)添加头文件
(2)添加库文件
5、生成解决方案,不会报错,但肯定没有结果,因为还没有添加动态链接库文件调用的python原函数。具体做法:在生成的Debug路径下添加调用的python原函数(例如:Test001.py),如下图所示
6、在运行程序过程中遇到的报错和解决办法:
(1)Cannot find or open the PDB file问题的解决
详见:http://blog.chinaunix.net/uid-11765716-id-3074932.html
(2)无法解析的外部符号 __imp__py_reftotal(或其他)
详见:https://www.jb51.net/article/108588.htm
二:进行C++/C调用Python的代码实现
1、c++ 调用python的过程:
1)初始化Python调用环境
2)加载对应的Python模块
3)加载对应的Python函数
4)将参数转化为Python元组类型
5)调用Python函数并传入参数元组
6)获取返回值
7)根据Python函数的定义解析返回值
注:在C中使用到的部分参数类型参照:详见:https://docs.python.org/3.6/c-api/arg.html
2、传递并返回数字
C部分:
//调用Add函数,传两个int型参数
void Add(){
int x=6,y=8;
Py_Initialize();
PyObject * pModule = NULL;
PyObject * pFunc = NULL;
PyObject *pReturn = NULL;
//加载python模块
pModule = PyImport_ImportModule("Test001");//Test001:Python文件名
//加载对应的Python函数
pFunc = PyObject_GetAttrString(pModule, "Add");//Add:Python文件中的函数名
//创建参数:
PyObject *pArgs = PyTuple_New(2);//函数调用的参数传递均是以元组的形式打包的,2表示参数个数
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", x));//0--序号,i表示创建int型变量
PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", y));//1--序号
//返回值
pReturn = PyEval_CallObject(pFunc, pArgs);//调用函数
//将返回值转换为int类型
int result;
PyArg_Parse(pReturn, "i", &result);//i表示转换成int型变量
printf("返回的结果result:%d + %d + 3= %d\n",x,y,result);
//cout << "6 + 8 = " << result << endl;
Py_Finalize();
}
Python部分:
def Add(a, b):
print(a,' + ',b,'=',a+b)
return a+b+3
运行结果:
3、传递并返回字符串
C部分:
void Hello(){
Py_Initialize();//调用Py_Initialize()进行初始化
PyObject * pModule = NULL;
PyObject * pFunc = NULL;
PyObject *pReturn = NULL;
pModule = PyImport_ImportModule("Test001");//调用的Python文件名
pFunc = PyObject_GetAttrString(pModule, "Hello");//调用的函数名
//创建参数:
PyObject *pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", "Hello Python!!"));
pReturn=PyEval_CallObject(pFunc, pArgs);//调用函数,并传入参数pArgs
char * result;
PyArg_Parse(pReturn, "s", &result);
printf("返回值:%s\n",result);
Py_Finalize();//调用Py_Finalize,和Py_Initialize相对应的.
}
Python部分:
def Hello(s):
print(s)
return "Hello C!!"
运行结果:
4、传递并返回一维list数组
C部分:
void szTest(){
/*Pass by List: Transform an C Array to Python List*/
double CArray[] = {1.2, 4.5, 6.7, 8.9, 1.5, 0.5};
Py_Initialize();
PyObject * pModule = NULL;
PyObject * pFunc = NULL;
PyObject *pDict = NULL;
PyObject *pReturn = NULL;
pModule = PyImport_ImportModule("Test001");
pDict = PyModule_GetDict(pModule);
pFunc = PyDict_GetItemString(pDict, "szTest");
cout<<"C Array Pass Into The Python List:"<<endl;
PyObject *PyList = PyList_New(6);//定义一个与数组等长的PyList对象数组
PyObject *ArgList = PyTuple_New(1);//定义一个Tuple对象,Tuple对象的长度与Python函数参数个数一直,上面Python参数个数为1,所以这里给的长度为1
for(int i = 0; i < PyList_Size(PyList); i++)
PyList_SetItem(PyList,i, PyFloat_FromDouble(CArray[i]));//给PyList对象的每个元素赋值
PyTuple_SetItem(ArgList, 0, PyList);//将PyList对象放入PyTuple对象中
pReturn=PyObject_CallObject(pFunc, ArgList);//调用函数,完成传递
if(PyList_Check(pReturn)){ //检查是否为List对象
printf("返回的结果result: ");
int SizeOfList = PyList_Size(pReturn);//List对象的大小,这里SizeOfList =
for(int i = 0; i < SizeOfList; i++){
PyObject *Item = PyList_GetItem(pReturn, i);//获取List对象中的每一个元素
int result;
PyArg_Parse(Item, "i", &result);//i表示转换成int型变量
printf("%d ",result);
//cout << PyInt_AsLong(Item) <<" "; //输出元素
Py_DECREF(Item); //释放空间
}
printf("\n");
}else{
cout<<"Not a List"<<endl;
}
Py_Finalize();
}
Python部分:
def szTest(List):
print (List)
IntegerList = [1, 2, 3]
return IntegerList
运行结果:
5、传递并返回二维list数组
list object(介绍C调用Python中,关于list的一些函数)
C部分:
void szSecTest(){
long CArray[2][3]={{2,3,4},{5,6,7}};
Py_Initialize();
PyObject * pModule = NULL;
PyObject * pFunc = NULL;
PyObject *pDict = NULL;
PyObject *pReturn = NULL;
pModule = PyImport_ImportModule("Test001");
pDict = PyModule_GetDict(pModule);
pFunc = PyDict_GetItemString(pDict, "szSecTest");
cout<<"C two-dim Array Pass Into The Python List:"<<endl;
PyObject *PyList = PyList_New(0);//定义该PyList对象为0和PyList_Append有关,相当于定义PyList为[]
PyObject *ArgList = PyTuple_New(1);
for(int i = 0; i <2; i++){
PyObject *PyList1 = PyList_New(3);
for(int j = 0; j < 3; j++){
PyList_SetItem(PyList1,j, PyLong_FromLong(CArray[i][j]));
}
PyList_Append(PyList,PyList1);//PyList_Append可以参考Python中,list的append的用处
}
PyTuple_SetItem(ArgList, 0, PyList);//将PyList对象放入PyTuple对象中
pReturn=PyObject_CallObject(pFunc, ArgList);//调用函数,返回一个list
if(PyList_Check(pReturn)){ //检查是否为List对象
int SizeOfList = PyList_Size(pReturn);//List对象的大小,这里SizeOfList = 2
printf("返回的结果result:\n");
for(int i = 0; i < SizeOfList; i++){
PyObject *ListItem = PyList_GetItem(pReturn, i);//获取List对象中的每一个元素
int NumOfItems = PyList_Size(ListItem);//List对象子元素的大小,这里NumOfItems = 3
for(int j = 0; j < NumOfItems; j++){
PyObject *Item = PyList_GetItem(ListItem, j);//遍历List对象中子元素中的每个元素
int result;
PyArg_Parse(Item, "i", &result);//i表示转换成int型变量
printf("%d ",result);
//cout << PyInt_AsLong(Item) <<" "; //输出元素
Py_DECREF(Item); //释放空间
}
cout<<endl;
Py_DECREF(ListItem); //释放空间
}
}else{cout<<"Not a List"<<endl;}
}
Python部分:
def szSecTest(List):
print (List)
IntegerList = [[1, 2, 3],[2,3,4]]
return IntegerList
运行效果:
6、传递并返回结构体
- 有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体.
简单举一个例子如下:
print('===== pack - unpack =====' )
buf1 = 2
buf2 = 3
buf4 = 4
retResult = struct.pack('ii4si', buf1, buf2, b'abd', buf4)
print(type(retResult)) #<class 'bytes'>
a1,a2,a3,a4=struct.unpack("ii4si",retResult)
print(a1,a2,bytes.decode(a3),a4)
结果图:
- struct.pack(fmt,v1,v2,v3...) 按fmt这个格式把后面数据给封装成指定的数据;返回一个包含了v1,v2等的字节对象,数据是通过fmt格式化后的。参数必须和fmt需要格式化的完全对应起来
- struct.unpack(fmt,buff) 按fmt这个格式把字节流转成原组tuple;pack的逆向操作,只有一个对象也返回tuple。
- 下面是fmt这个格式在python3.6的说明:详细请见:struct 的官方文档(Format)
2)Python中str与bytes的互相转换
a = "asdf"
b = a.encode('utf-8')#str转化为bytes
c = bytes.decode(b)#bytes转化为str
print(a,type(a),b,type(b),c,type(c))
结果图:
3)以下部分为C调用Python,传递结构体的代码部分
C部分:
typedef struct header_ {
int buf1;
int buf2;
char buf3[12];
int buf4;
}header;
void testStruct(){
// 初始化Python
Py_Initialize();
//PyObject *pName = PyBytes_FromString("Test001");PyObject *pModule = PyImport_Import(pName); //调用Python的Module的另一种方法
PyObject * pModule = NULL;
PyObject *pDict = NULL;
PyObject * pFunc = NULL;
pModule = PyImport_ImportModule("Test001");
if (!pModule) {
printf("can't find Test001.py");
return ;
}
pDict = PyModule_GetDict(pModule);
if (!pDict) return ;
//下面这段是查找函数testStruct并执行testStruct
pFunc = PyDict_GetItemString(pDict, "testStruct");
if (!pFunc || !PyCallable_Check(pFunc)) {
printf("can't find function [testStruct]");
return ;
}
//创建结构体
header input;
memset(&input,0,sizeof(input));
input.buf1 = 1;
input.buf2 = 2;
input.buf4 = 3;
strcpy_s(input.buf3, "kjaf");
//打包成string
char * byInput = new char(sizeof(input));
memcpy(byInput, &input, sizeof(input));
//申请python入参
PyObject *pArgs = PyTuple_New(1);
//对python入参进行赋值; s代表char*格式, #代表传入指定长度
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s#",byInput , sizeof(input)));
//PyObject *Pystructs = Py_BuildValue("s#",byInput, sizeof(input));//为了检验入参是否为空
//if (Pystructs == nullptr){printf("Pystructs is nullptr"); }
//执行函数
PyObject *pResult = PyObject_CallObject(pFunc, pArgs);
char* pRsp;
//获取返回值
PyArg_Parse(pResult, "s#", &pRsp); //由于传回的参数为bytes(经过Python的unpack后,传回的参数),故用"s#",而不是"s"
//转成结构体
header* pstRsp = (header*)pRsp;//把string转化为结构体
printf("\n-----------c++层接收py返回:\nbuf1:%d , buf2:%d , buf3:%s , buf4:%d\n\n",
pstRsp->buf1, pstRsp->buf2, pstRsp->buf3, pstRsp->buf4);
Py_Finalize();
}
Python部分:
import struct
def testStruct(a):
a = a.encode('utf-8')#要注意把从C传过来的string转化为bytes,以便struct.unpack解析
ret = struct.unpack('i i 12s i', a)
print("--------------------python receive c++ struct:\n")
print(ret)
buf1 = ret[0] + 1
#print('buf1: ',buf1)
buf2 = ret[1] + 1
buf4 = ret[3] + 1
print("--------------------begin pack data and begin send to c++\n")
bin_buf_all = struct.pack('ii12si', buf1, buf2,b"dfds", buf4) #struct.pack后,bin_buf_all为bytes类型
print("----------------------------python end-----------------------")
return bin_buf_all
运行结果:
注:结构体显然就是一种的数据组织方式,使用结构体要注意数据对齐的问题,有兴趣的话可以看一下:https://blog.****.net/mirale/article/details/38128709
参考博文:
混合编程之——C++调用python2.7&python3.5
C与python的调用一(导入python模块与,获得函数与类)
Python + C/C++ 嵌入式编程(1):多维数组Numpy.Array()在Python和C/C++文件间的传递问题
c++调用python系列(1): 结构体作为入参及返回结构体