环境

VS2005Python2.5.4 Windows XP SP3

简述

一般开发过游戏的都知道Lua和C++可以很好的结合在一起,取长补短,把Lua脚本当成类似动态链接库来使用,很好的利用了脚本开发的灵活性。而作为一门流行的通用型脚本语言python,也是可以做到的。在一个C++应用程序中,我们可以用一组插件来实现一些具有统一接口的功能,一般插件都是使用动态链接库实现,如果插件的变化比较频繁,我们可以使用Python来代替动态链接库形式的插件(堪称文本形式的动态链接库),这样可以方便地根据需求的变化改写脚本代码,而不是必须重新编译链接二进制的动态链接库。灵活性大大的提高了。

Python/CAPI简介

通过C++调用Python脚本主要要用到如下的一些Python提供的API,因为实际上C++要调用的是Python的解释器,而Python解释器本质就是实现在动态链接库里面的,因此在调用前和调用后要进行一些初始化和资源释放的工作,另外,要调用Python脚本里面的函数等等东西,需要Python提供的一些特殊API来包装C++调用。(可以参考[2])。

void Py_Initialize(void)

初始化Python解释器,如果初始化失败,继续下面的调用会出现各种错误,可惜的是此函数没有返回值来判断是否初始化成功,如果失败会导致致命错误。

int Py_IsInitialized(void)

检查是否已经进行了初始化,如果返回0,表示没有进行过初始化。

void Py_Finalize()

反初始化Python解释器,包括子解释器,调用此函数同时会释放Python解释器所占用的资源。

int PyRun_SimpleString(const char *command)

实际上是一个宏,执行一段Python代码。

PyObject* PyImport_ImportModule(char *name)

导入一个Python模块,参数name可以是*.py文件的文件名。类似Python内建函数import。

PyObject* PyModule_GetDict( PyObject *module)

相当于Python模块对象的__dict__属性,得到模块名称空间下的字典对象。

PyObject* PyRun_String(const char* str, int start,PyObject* globals, PyObject* locals)

执行一段Python代码。

int PyArg_Parse(PyObject* args, char* format, ...)

把Python数据类型解析为C的类型,这样C程序中才可以使用Python里面的数据。

PyObject* PyObject_GetAttrString(PyObject *o, char*attr_name)

返回模块对象o中的attr_name 属性或函数,相当于Python中表达式语句,o.attr_name。

PyObject* Py_BuildValue(char* format, ...)

和PyArg_Parse刚好相反,构建一个参数列表,把C类型转换为Python对象,使得Python里面可以使用C类型数据。

PyObject* PyEval_CallObject(PyObject* pfunc, PyObject*pargs)

此函数有两个参数,而且都是Python对象指针,其中pfunc是要调用的Python 函数,一般说来可以使用PyObject_GetAttrString()获得,pargs是函数的参数列表,通常是使用Py_BuildValue()来构建。

更多的API请参考官方的文档,比较直观简单,譬如怎样初始化一个类实例,怎样调用类成员函数。下面上点代码,感受下这个过程。

C++代码

#include "stdafx.h"

#include "Python.h"

int _tmain(int argc, _TCHAR* argv[])

{

int nRet = -1;

PyObject* pName = NULL;

PyObject* pModule =NULL;

PyObject* pDict = NULL;

PyObject* pFunc = NULL;

PyObject* pArgs = NULL;

PyObject* pRet = NULL;

do

{

// 初始化Python

// 在使用Python系统前,必须使用Py_Initialize对其

// 进行初始化。它会载入Python的内建模块并添加系统路

// 径到模块搜索路径中。这个函数没有返回值,检查系统

// 是否初始化成功需要使用Py_IsInitialized。

Py_Initialize();

// 检查初始化是否成功

if (!Py_IsInitialized())

{

break;

}

// 添加当前路径

// 把输入的字符串作为Python代码直接运行,返回

// 表示成功,-1表示有错。大多时候错误都是因为字符串

// 中有语法错误。

PyRun_SimpleString("importsys");

PyRun_SimpleString("sys.path.append('./')");

// 载入名为PyPlugin的脚本

pName = PyString_FromString("PyPlugin");

pModule = PyImport_Import(pName);

if (!pModule)

{

printf("can't findPyPlugin.py\n");

break;

}

pDict = PyModule_GetDict(pModule);

if (!pDict)

{

break;

}

// 找出函数名为AddMult的函数

pFunc = PyDict_GetItemString(pDict, "AddMult");

if (!pFunc || !PyCallable_Check(pFunc))

{

printf("can't findfunction [AddMult]\n");

break;

}

pArgs = Py_BuildValue("ii", 12, 14);

PyObject* pRet = PyEval_CallObject(pFunc,pArgs);

int a = 0;

int b = 0;

if (pRet && PyArg_ParseTuple(pRet,"ii", &a,&b))

{

printf("Function[AddMult] call successful a + b = %d, a * b = %d\n", a, b);

nRet = 0;

}

if (pArgs)

Py_DECREF(pArgs);

if (pFunc)

Py_DECREF(pFunc);

// 找出函数名为HelloWorld的函数

pFunc = PyDict_GetItemString(pDict, "HelloWorld");

if (!pFunc || !PyCallable_Check(pFunc))

{

printf("can't findfunction [HelloWorld]\n");

break;

}

pArgs = Py_BuildValue("(s)", "magictong");

PyEval_CallObject(pFunc,pArgs);

} while (0);

if (pRet)

Py_DECREF(pRet);

if (pArgs)

Py_DECREF(pArgs);

if (pFunc)

Py_DECREF(pFunc);

if (pDict)

Py_DECREF(pDict);

if (pModule)

Py_DECREF(pModule);

if (pName)

Py_DECREF(pName);

Py_Finalize();

return 0;

}

Python代码

#!/usr/bin/python

import string

class CMyClass:

def HelloWorld(self):

print 'HelloWorld'

class SecondClass:

def invoke(self,obj):

obj.HelloWorld()

def HelloWorld(strName):

print "Hello ", strName

def Add(a, b, c):

return a + b + c

def AddMult(a, b):

"""

"""

print "in FunctionAddMult..."

print a

print b

return a + b, a * b

def StringToUpper(strSrc):

return string.upper(strSrc)

下面还有几个比较重要的问题需要解决,且听慢慢道来。

C++怎么向Python传递参数

C++向Python传参数是以元组(tuple)的方式传过去的,因此我们实际上就是构造一个合适的Python元组就可以了,要用到PyTuple_New,Py_BuildValue,PyTuple_SetItem等几个函数,其中Py_BuildValue可以有其它一些的替换函数。

PyObject* pyParams = PyTuple_New(2);

PyObject* pyParams1= Py_BuildValue("i",5);

PyObject* pyParams2= Py_BuildValue("i",6);

PyTuple_SetItem(pyParams,0, pyParams1);

PyTuple_SetItem(pyParams,1, pyParams2);

pRet = PyEval_CallObject(pFunc, pyParams);

也可以直接使用PyObject* Py_BuildValue(char *format, ...) 函数来直接来构造tuple,此函数的使用也很简单,记住一些转换的格式常量即可轻松进行转换(格式常量有点类似printf,参考[9])。譬如s 表示字符串,i表示整型变量,f表示浮点数,o表示一个Python对象等等。

Py_BuildValue("")                       None

Py_BuildValue("i",123)                 123

Py_BuildValue("iii",123, 456, 789)     (123, 456, 789)

Py_BuildValue("s","hello")             'hello'

Py_BuildValue("ss","hello", "world")    ('hello', 'world')

Py_BuildValue("s#","hello", 4)         'hell'

Py_BuildValue("()")                     ()

Py_BuildValue("(i)",123)               (123,)

Py_BuildValue("(ii)",123, 456)         (123, 456)

Py_BuildValue("(i,i)",123, 456)        (123, 456)

Py_BuildValue("[i,i]",123, 456)        [123, 456]

Py_BuildValue("{s:i,s:i}",

"abc", 123, "def", 456)    {'abc': 123, 'def': 456}

Py_BuildValue("((ii)(ii))(ii)",

1, 2, 3, 4, 5, 6)         (((1, 2), (3, 4)), (5, 6))

C++怎么转换Python的返回值

Python传回给C++的都是PyObject对象,因此可以调用Python里面的一些类型转换API来把返回值转换成C++里面的类型。类似PyInt_AsLong,PyFloat_AsDouble这些系列的函数。Python比较喜欢传回一个元组,可以使用PyArg_ParseTuple这个函数来解析。这个函数也要用到上面的格式常量(参考[10])。还有一个比较通用的转换函数是PyArg_Parse,也需要用到格式常量,够不够强大,用了就知道了。

直接调用Python脚本文件——另一种调用方式

初始化,反初始化都一样,此种方式其实就是直接调用PyRun_SimpleString函数。

if(fp && PyRun_SimpleString("execfile('PyFile.py')") != 0)

{

fclose(fp);

printf("PyRun_SimpleFile(%s)failed!", szFile);

return -1;

}

还有一种方法是调用PyRun_SimpleFile()函数来直接运行一个Python文件,不过这种方式有点危险,因为这个API要求传入一个FILE指针,而微软的几个CRT版本FILE指针的定义有了变化,因此传入你使用VS2005编译的FILE指针或者其它版本的FILE极有可能崩溃,如果你想安全调用,最好是自己把Python的源代码使用和应用程序相同的环境一起编译出lib来使用。

char szFile[] = "PyFile.py";

FILE* fp = fopen(szFile, "r");

if(fp && PyRun_SimpleFile(fp,szFile) != 0)

{

fclose(fp);

printf("PyRun_SimpleFile(%s)failed!", szFile);

return -1;

}

参考文献

[1] python官网 http://www.python.org/

[2] python/c APIReference Manual http://docs.python.org/2/c-api/index.html

[3] 用C语言扩展python的功能 https://www.ibm.com/developerworks/cn/linux/l-pythc/

[4] C++扩展和嵌入Python http://www.vckbase.com/index.php/wv/1258

[5] Python调用C/C++模块 http://blog.csdn.net/masefee/article/details/4750920

[6] Extendingand Embedding the Python Interpreter

http://docs.python.org/2/extending/index.html

[7] EmbeddingPython in Another Application

http://docs.python.org/2/extending/embedding.html

[8] C调用Python类/函数简单代码

http://www.360doc.com/content/12/0506/13/9369336_209021809.shtml

[9] The Py_BuildValue()Function

http://docs.python.org/release/1.5.2p2/ext/buildValue.html

[10] FormatStrings for PyArg_ParseTuple()

http://docs.python.org/release/1.5.2p2/ext/parseTuple.html

[11]Python编程

http://wiki.woodpecker.org.cn/moin/PP3eD

http://blog.csdn.net/magictong/article/details/8947892

C++调用Python浅析的更多相关文章

  1. 浅析 C++ 调用 Python 模块

    浅析 C++ 调用 Python 模块 作为一种胶水语言,Python 能够很容易地调用 C . C++ 等语言,也能够通过其他语言调用 Python 的模块. Python 提供了 C++ 库,使得 ...

  2. [转] C/C++ 调用Python

    from :  https://cyendra.github.io/2018/07/10/pythoncpp/ 目录 前言 官方文档 环境搭建 编译链接 Demo 解释器 初始化 GIL Object ...

  3. python 浅析模块,包及其相关用法

    今天买了一本关于模块的书,说实话,模块真的太多了,小编许多也不知道,要是把模块全讲完,可能得出本书了,所以小编在自己有限的能力范围内在这里浅析一下自己的见解,同时讲讲几个常用的模块. 这里是2018. ...

  4. cpp 调用python

    在用cpp调用python时, 出现致命错误: no module named site  ,  原因解释器在搜索路径下没有找到python库.可以在调用Py_Initialize前,调用 Py_Se ...

  5. c调用python

    #include <Python.h>//python33(python2.x有几个函数不对应) /* PyImport_ImportModule 导入一个Python模块并返回它的指针 ...

  6. linux+php+apache web调用python脚本权限问题解决方案

    lamp : linux + apache + mysql + php 在上篇随笔中linux+php+apache调用python脚本时出现的问题的根本原因是:apache运行时使用的apache用 ...

  7. linux+php+apache web调用python脚本权限问题

    lamp : linux + apache + mysql + php 在近期项目中使用 linux + apache + php调用python脚本是出现以下权限问题: build/bdist.li ...

  8. C#中调用python方法

    最近因为项目设计,有部分使用Python脚本,因此代码中需要调用python方法. 1.首先,在c#中调用python必须安装IronPython,在 http://ironpython.codepl ...

  9. PHP 调用Python脚本

    上次做用户反馈自动翻译,写了个python脚本,将日文的用户反馈翻译成中文,效果虽然可以,但其它不懂python的童鞋就没法使用了,所以搭了个web服务,让其他人可以通过网页访问查询.使用的是apac ...

随机推荐

  1. [tmux] Manage terminal workspaces using session naming

    It's a lot easier to manage your tmux session when they have sensible names. We'll cover: How to cre ...

  2. [Angular] ChangeDetection -- onPush

    To understand how change detection can help us improve the proference, we need to understand when it ...

  3. Kolya and Tandem Repeat

     Kolya and Tandem Repeat time limit per test 2 seconds memory limit per test 256 megabytes input s ...

  4. sql for xml 还有一种写法(採用 tag 与 union all,简洁易懂)

    sql for xml 还有一种写法(採用 tag 与 union all,简洁易懂) 測试环境:sql 08, 08 R2, 2010,  2012, 2014 等 declare @agent t ...

  5. 小强的HTML5移动开发之路(44)——JqueryMobile中的按钮

    一.链接按钮 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...

  6. 自旋锁解决StackOverflowError案例

    本节笔者分享一个在实际工作中遇到的栈内存溢出(StackOverflowError)问题,以及其解决方案. 问题介绍:笔者负责的一个Java Web项目在启动的时候,需要有一些初始化操作,而接下来的代 ...

  7. [Django] Get started with Django -- Install python and virtualenv

    Install python3 on MacOS: brew install python3 Come alone with python3, there are also some other to ...

  8. NOIP模拟 poke - 贪心

    题目大意: 给一副牌(\(n \le 1000000, a_i \le 1000000\)),问最多能打出多少对对子(x, x)或者顺子(x, x + 1, x + 2). 题目分析: 感觉跟斗地主很 ...

  9. Qt多线程和GUI界面假死(run()是线程的入口,就像main()对于应用程序的作用。分析QThread::exec函数的源码,旧的QMutexLocker模式其实很好用,挡住别人进入抢占资源,可照抄)good

    QThread的常见特性: run()是线程的入口,就像main()对于应用程序的作用.QThread中对run()的默认实现调用了exec(),从而创建一个QEventLoop对象,由其处理该线程事 ...

  10. CSS布局--左侧自适应母元素高度

    平常项目中经常会遇到有左侧导航菜单的高度不固定,需要与母元素或右侧元素等高的情况,以前就自以为是的使用js来设置,不仅不方便还会出现各种bug,后来就突然想到了一个好方法.有可能这方法已经被其他人用烂 ...