以前项目中是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 = ;
//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 = ;
char* pName = NULL;
if (!PyArg_ParseTuple(args, "is", &personAddr, &pName)) {
return NULL;
}
Person* p = (Person*)personAddr;
p->setName(pName); return Py_BuildValue("i", );
} static PyObject* GetName(PyObject* self, PyObject* args) {
int personAddr = ;
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, , 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 PythonWrap def 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 = ;
char* pName = NULL;
if (!PyArg_ParseTuple(args, "is", &personAddr, &pName)) {
return NULL;
}
Person* p = (Person*)personAddr;
p->setName(pName); return NULL;
}

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

C++嵌入Python,以及两者混用的更多相关文章

  1. freeswitch嵌入python脚本

    操作系统:debian8.5_x64 freeswitch 版本 : 1.6.8 python版本:2.7.9 开启python模块 安装python lib库 apt-get install pyt ...

  2. 在应用中嵌入Python:转

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

  3. Hive 11、Hive嵌入Python

    Hive嵌入Python Python的输入输出都是\t为分隔符,否则会出错,python脚本输入print出规定格式的数据 用法为先add file,使用语法为TRANSFORM (name, it ...

  4. 工程脚本插件方案 - c集成Python基础篇(VC++嵌入Python)

    序: 为什么要集成脚本,怎么在工程中集成Python脚本. 在做比较大型的工程时,一般都会分核心层和业务层.核心层要求实现高效和稳定的基础功能,并提供调用接口供业务层调用的一种标准的框架划分.在实际中 ...

  5. 嵌入Python | 调用Python模块中有参数的函数

    开发环境Python版本:3.6.4 (32-bit)编辑器:Visual Studio CodeC++环境:Visual Studio 2013 需求说明前一篇<在C++中嵌入Python|调 ...

  6. c++中嵌入python

    c++ 中嵌入python  :  https://blog.csdn.net/yiyouxian/article/category/6324494 Python C 和线程 :http://www. ...

  7. 【转】C++中嵌入python程序——参数传递

    C++中嵌入python程序——参数传递 前面两篇博客已经介绍如何在C++中嵌套使用 python,但是在实际使用中,我们需要向python传递各种各样的参数,这样的程序才具有更高的灵活性.下面简单介 ...

  8. 在C语言中如何嵌入python脚本

    最近在写配置文件时,需要使用python脚本,但脚本是一个监控作用,需要它一直驻留在linux中运行,想起C语言中能够使用deamon函数来保留一个程序一直运行,于是想到写一个deamon,并在其中嵌 ...

  9. 《扩展和嵌入python解释器》1.4 模块方法表和初始化函数

    <扩展和嵌入python解释器>1.4 模块方法表和初始化函数   1.4 模块方法表和初始化函数 下面,我演示如何从Python程序调用spam_system().首先,我们需要在’方法 ...

随机推荐

  1. 类函数和对象函数 PHP

    1. bool class_exists ( string $class_name [, bool $autoload = true ] )  检查指定的类是否已定义. 如果由 class_name  ...

  2. 使用Rsync进行文件的同步与备份

    Rsync是Linux下非常不错的文件同步备份工具,安全性高.备份迅速.支持增量备,功能强大且高效. 服务端配置 Rsync服务的配置文件/etc/rsyncd.conf,示例配置(带用户名密码配置) ...

  3. Quick-Cocos2d-x初学者游戏教程1

    Quick-Coco2d-x安装: Quick 安装完成后,在它的根目录下可以找到有两个名为setup_mac.sh.setup_win.bat的批处理脚本,它们分别是搭建Mac和Windows开发环 ...

  4. 《BI项目笔记》数据源视图设置

    目的数据源视图是物理源数据库和分析维度与多维数据集之间的逻辑数据模型.在创建数据源视图时,需要在源数据库中指定包含创建维度和多维数据集所需要的数据表格和视图.BIDS与数据库连接,读取表格和视图定义, ...

  5. hdu4758Walk Through Squares(ac自动机+dp)

    链接 dp[x][y][node][sta] 表示走到在x,y位置node节点时状态为sta的方法数,因为只有2个病毒串,这时候的状态只有4种,根据可走的方向转移一下. 这题输入的是m.N,先列后行, ...

  6. [java基础]循环结构1

    [java基础]循环结构1 循环结构:for循环,while循环,do_while循环在,增强型for循环 /** 文件路径:G:\JavaByHands\循环语句\ 文件名称:WhileTest.j ...

  7. Thread-0" kafka.common.FailedToSendMessageException: Failed to send messages after 3 tries.

    http://blog.csdn.net/jingshuigg/article/details/25001979 zookeeper.connect=localhost:2181改成zookeeper ...

  8. jsonp 跨域 能返回数据但 无法返回成功问题

    apihandler.ashx?callback=eqfeed_callback:1Uncaught SyntaxError: Unexpected token : 原因在于jsonp传的数据格式不一 ...

  9. SAP的物料归档

    我们在对前台对物料进行删除时,是物理删除,也就是打个删除标志,并没有正真的从数据库里删除,在前台还是可以看到的,下面介绍一下SAP的归档处理可以 把已删除的物料在前台删除掉,注意:项目里根据情况得到领 ...

  10. SAP无输入历史记录(已在本地数据开启历史记录)解决方法

    SAP客户端已开启本地数据的历史记录,但是仍然没有录入记录,重装SAP无法解决问题,没有最近输入记录操作极为不方便,经研究表现出的问题特征如下:1.同一用户在另一台电脑使用SAP就有历史记录,到了本电 ...