以前项目中是C++嵌入Python,开发起来很便利,逻辑业务可以放到python中进行开发,容易修改,以及功能扩展。不过自己没有详细的研究过C++嵌入python的细节,这次详细的研究一下。首先我们简单的使用C++调用一个Python的py脚本,然后通过Python使用C++中的对象和方法。我们使用的Python是2.7.11

  1. 使用C++使用python的功能,比如我们写一个show.py,代码如下:  

def show(name):return "hello " + name

  这个python脚本实在是太简单了,不需要任何解释了。然后简单的写一个C++函数,来简单的调用这个show.py中的函数show:

#include <Python.h>
#include <iostream>
using namespace std;void python_test() {Py_SetPythonHome("D:/Python27");Py_Initialize();PyObject* pModule = PyImport_ImportModule("show");if (!pModule) {cout << "import python module test failed." << endl;return;}PyObject* pFunc = PyObject_GetAttrString(pModule, "show");if (!pFunc) {cout << "import python func failed." << endl;return;}PyObject* pParam = Py_BuildValue("(s)", "hahaha");PyObject* pResult = PyObject_CallObject(pFunc, pParam);if (pResult) {char* pBuf = NULL;int nBufSize = 0;//if (PyArg_ParseTuple(pResult, "si", &pBuf, &nBufSize))//{//    cout << "buf=" << pBuf << endl;//    cout << "buf size=" << nBufSize << endl;//}if (PyArg_Parse(pResult, "s", &pBuf)){cout << "buf=" << pBuf << endl;}}Py_DECREF(pParam);Py_DECREF(pResult);Py_Finalize();return;
}int main() {python_test();
}

  这里需要注意几个地方:

  (1) 首先Py_SetPythonHome("D:/Python27");这条语句是用来设置Python脚本的目录的,如果不设置这个目录,我们就不知道Python去哪个目录去读取我们的脚本了

  (2) Py_BuildValue("(s)", "hahaha"); 即使这里只需要一个参数,也需要使用tuple这样的方式传递参数,否则python解析模块会报错,它内部做了参数类型判断。

  (3) 如果show函数返回的是一个普通参数,我们使用PyArg_Parse来解析, 如果是一个元组,我们使用PyArg_ParseTuple来解析。不能混用,否则内部也会报错。

  从代码上看起来也是非常的简单,不过一开始部署环境不是这么简单,因为我们默认安装的Python是没有python27_d.lib 和python27_d.dll这2个文件的,具体如何解决python环境的问题,看第二节。

  

  2. python代码编译

  之前说了默认安装的Python是没有python27_d.lib 和python27_d.dll这2个文件的,我尝试过从网上单独下载这2个文件,不过小版本号不匹配也会出现运行时的crash。所以我选择了从网上下载了python2.7.11源代码,然后自己进行编译。不过这个编译总的来说是非常简单的,我是在windows下使用vs2012编译的,主要需要注意一下几点:

  (1) 使用PCbuild中的pcbuild.sln,直接打开,然后再vs中选中python项目,直接编译,我的编译过程是没有出现任何错误的,有些警告,我们直接忽略。在debug模式下,直接生成python27_d.lib 和python27_d.dll,还有python27_d.exe

  (2) 虽然我们有了python27_d.lib 和python27_d.dll,然后再在之前测试工程中添加好include和lib的目录,编译的时候,会提示找不到pyconfig.h这个文件。 这是因为这个文件是windows下特有的,我们需要在PC目录中copy这个文件到include目录下,然后再编译,就可以正常完成了。

  

  3. python调用C++对象

  python调用C++接口,有多种办法。通常python调用C++接口是一种功能扩展,将C++功能编译成动态库,然后python通过ctypes来导入库文件,在windows下是dll,linux下是so文件,这个使用比较简单,而且例子非常多,就不再介绍了。在很多复杂项目中,比如游戏中,我们希望python中操作玩家对象,那么如果将这个模块导成库文件,非常麻烦,而且不方便操作玩家对象实例。我们采用动态的方式来进行:

  示例中我们新建2个c++类,一个Person,一个PersonWrap。 Person对象代表某个人,PersonWrap是针对Person的Python接口扩展,Person.h代码如下:

#ifndef PERSON_H
#define PERSON_H#include <string>
using namespace std;class Person {
public:Person(){}~Person(){}void setName(const string& _name) {name = _name;}string& getName() {return name;}private:string name;
};#endif

  PersonWrap.h代码如下:

#ifndef PERSON_WRAP_H
#define PERSON_WRAP_H#include <Python.h>
#include "Person.h"class PersonWrap {
public:static PyObject* SetName(PyObject* self, PyObject* args) {cout << "Wrap setName" << endl;int personAddr = 0;char* pName = NULL;if (!PyArg_ParseTuple(args, "is", &personAddr, &pName)) {return NULL;}Person* p = (Person*)personAddr;p->setName(pName);return Py_BuildValue("i", 0);}static PyObject* GetName(PyObject* self, PyObject* args) {int personAddr = 0;if (!PyArg_ParseTuple(args, "i", &personAddr)) {return NULL;}Person* p = (Person*)personAddr;string& name = p->getName();return Py_BuildValue("s", name.c_str());}static PyObject* GetDesc(PyObject* self, PyObject* args) {return Py_BuildValue("s", "whoknows");}static PyObject* CreatePerson(PyObject* self, PyObject* args) {cout << "Wrap create person" << endl;char* pName = NULL;if (!PyArg_ParseTuple(args, "s", &pName)) {return NULL;}Person* p = new Person();p->setName(pName);return Py_BuildValue("i", p);}
};PyMethodDef WrapMethods[] = {{"SetName", PersonWrap::SetName, METH_VARARGS, "SetName"},{"GetName", PersonWrap::GetName, METH_VARARGS, "GetName"},{"GetDesc", PersonWrap::GetDesc, METH_VARARGS, "GetDesc"},{"CreatePerson", PersonWrap::CreatePerson, METH_VARARGS, "CreatePerson"},{NULL, NULL, 0, NULL}
};PyMODINIT_FUNC initPythonWrap() {PyObject* module;module = Py_InitModule("PythonWrap", WrapMethods);
}#endif

  Person类的代码非常简单,不需要解释了。PersonWrap针对Person的接口进行了封装,可以看出PersonWrap中都是static函数,这是为了能够导出接口供Python使用,同时定义一个WrapMethods数组,将这些接口用Python方法的定义方式导出。最后用一个initPythonWrap方法来对这个模块进行动态初始化。这样我们的ptyhon脚本就可以调用了。

  不过需要在main函数中,新增initPythonWrap();的调用,否则不会生效。我们测试的python代码如下:

import sys
import PythonWrapdef show(name):person = PythonWrap.CreatePerson("tom")PythonWrap.SetName(person, "jim")return "hello " + PythonWrap.GetName(person)

  从python脚本可以看出,我们import了我们动态生成的PythonWrap模块,然后就能像其他普通python模块一样随意的调用其接口。需要注意的是,我们的Person对象是一个C++对象,根据文档查询,如果不利于第三方插件是不能直接将C++对象传递给Python的,所以我们在传出和传入的时候,是将Person对象指针通过整数的方式进行传递的,然后强制进行指针转换。

  如果在debug模式下,在我们调用python的show函数时,使用到了PythonWrap中的接口时,会进入我们的C++代码,调试起来还是比较方便的。不过还存在一些问题,就是如果我们的SetName接口写成这样:

static PyObject* SetName(PyObject* self, PyObject* args) {cout << "Wrap setName" << endl;int personAddr = 0;char* pName = NULL;if (!PyArg_ParseTuple(args, "is", &personAddr, &pName)) {return NULL;}Person* p = (Person*)personAddr;p->setName(pName);return NULL;}

  那么这个接口就只能被调用一次,第二次调用的时候就不会再触发,应该是针对返回值,python模块代码中做了特殊的处理,现在还不知道具体原因。等后面弄明白之后再来补充。

转载于:https://www.cnblogs.com/chobits/p/5045946.html

C++嵌入Python,以及两者混用相关推荐

  1. 在C/C++中嵌入Python

    在C/C++中嵌入Python也比较简单,首先需要在VC中添加Python的include文件目录和lib文件目录: VC6.0下,打开tools->options->directorie ...

  2. 在python的解释器中使用函数_浅析Windows 嵌入python解释器的过程

    这次主要记录在windows下嵌入 python 解释器的过程,程序没有多少,主要是头文件与库文件的提取. 程序平台:windows10 64 bit. Qt 5.5.1  MSVC  2013 32 ...

  3. UE4 嵌入Python

    1.参考C++嵌入Python 2.UE4的新建插件HuGanTool,找到相应的Build.cs文件,手动添加第三方库 private string ModulePath{get{return Mo ...

  4. python嵌入到程序_在应用中嵌入Python:转

    前面的章节讨论如何扩展Python,如何生成适合的C库等.不过还有另一种情况:通过将Python嵌入C/C++应用以扩展程序的功能.Python嵌入实现了一些使用Python更合适的功能.这可以有很多 ...

  5. 扩展和嵌入Python解释器 Extending and Embedding the Python Interpreter

    2007年开始使用Python与C的交互编程,那时分享了一篇<使用C/C++扩展Python> http://gashero.yeax.com/?p=38 .8年过去了,很多技术时过境迁, ...

  6. 在应用中嵌入Python

    翻译: gashero 前面的章节讨论如何扩展Python,如何生成适合的C库等.不过还有另一种情况:通过将Python嵌入C/C++应用以扩展程序的功能.Python嵌入实现了一些使用Python更 ...

  7. 在应用中嵌入Python - lf8289的专栏 - CSDNBlog

    在应用中嵌入Python - lf8289的专栏 - CSDNBlog " 在应用中嵌入Python收藏 新一篇: 软件在线升级设计方案及演变过程分析 | 旧一篇: 在windows下面关闭 ...

  8. c中嵌入Python,提供灵活性

    我的项目进行到这个时候才发现仅仅只有c还是不能完成所有的事情,为了提供更好的个性化应用,需要一个解释性语言加入到体系中来,对比发现Python是最好的选择,不管是从性能上讲还是内存管理等各方面将都有独 ...

  9. python如何调用cpp文件的接口函数_C++中嵌入Python调用

    python嵌入到C++中 把python嵌入的C++里面需要做一些步骤 安装python程序,这样才能使用python的头文件和库 在我们写的源文件中增加"Python.h"头文 ...

最新文章

  1. 基于单个xml的数据库
  2. 查看linux下各数据类型的大小
  3. Appweb(CVE-2018-8715)漏洞复现与思考
  4. 程序员懂点经济学-股票投资
  5. php date 有warning,PHP Warning: strtotime()错误解决办法
  6. svn和GitHub的使用
  7. 简约不简单的单例模式
  8. jmeter(十三)常见问题及解决方法
  9. Nginx中修改php.ini的上传设置upload_max_filesize的值
  10. 计算机硬件英语单词有哪些,计算机硬件英语词汇
  11. CFA一级考试题型是什么?好不好考?
  12. google linux桌面快捷方式,centos7 rhel7 linux下怎么安装google chrome 设置谷歌浏览器桌面快捷方式...
  13. A short theory of channel flow
  14. 高级创意,单片机电子DIY制作精华资料汇总
  15. 【教学类-16-01】20221121《数字卡片9*2》(中班)
  16. BZOJ 1101([POI2007]Zap-满足x=ay=bgcd(x,y)=d的数对个数)
  17. Java开发工程师个人简历模板,简洁,给人正规,严谨的形象
  18. NVIDIA显卡虚拟化vGPU终于支持KVM了
  19. [Gitlab CI/CD] Error loading key “/dev/fd/63“: invalid format
  20. VNote - Markdown 笔记工具

热门文章

  1. [原创]FineUI秘密花园(二十六) — 选项卡控件概述
  2. 一些看起来有用但没用过的函数
  3. AD的备份与标准还原:深入浅出Active Directory系列(四)
  4. 详述SaltStack Salt 命令注入漏洞(CVE-2020-16846/25592)
  5. Meltdown和Foreshadow等补丁被指不完整,处理器易受新攻击
  6. java密钥长度受限制问题解决
  7. HTTP之Range
  8. iOS截取视频预览图,截图方向错误的解决
  9. C语言中控制printf的打印颜色实例及vt100的控制符文档-转
  10. [Java] 蓝桥杯ADV-171 算法提高 身份证号码升级