C++调用Python浅析

环境

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

时间: 2024-12-15 07:02:03

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

浅析 C++ 调用 Python 模块

浅析 C++ 调用 Python 模块 作为一种胶水语言,Python 能够很容易地调用 C . C++ 等语言,也能够通过其他语言调用 Python 的模块. Python 提供了 C++ 库,使得开发者能很方便地从 C++ 程序中调用 Python 模块. 具体的文档参考官方指南: Embedding Python in Another Application 调用方法 1 链接到 Python 调用库 Python 安装目录下已经包含头文件( include 目录)和库文件 ( Windo

C#调用python文件执行

我的电脑环境是使用.net framework4.5.1,如果在调试过程中调不通请注意 我用的是Visual studion 2017,python组件下载地址:http://ironpython.codeplex.com/releases/view/ 下载的版本是2.7,下载安装完之后记得引入安装路径下的以下三个dll (1)首先先说一个简单的功能,在c#代码中执行python字符串,内容如下: (2)c#调用python文件: 在当前目录下新建一个后缀名为py的文件,文件名为AmoutDis

编程:C#调用Python模块

当下,C#与Python都是比较热门的计算机编程语言,他们各有优缺点,如果能让他们互相配合工作,那是多么美好的事情,今天我来讲解一下如何利用C#来调用Python. 如果让C#支持调用Python模块,我们首先需要安装一些扩展,这里推荐使用IronPython库. 第一步,我们需要下载IronPython库的安装包,这里请大家移步官网 http://ironpython.codeplex.com/ ,下载并安装相关库文件. 第二步,我们新建一个C#控制台测试项目,并将IronPython安装目录

使用c语言调用python小结

最近在做一个漏洞展示平台,攻击实现部分使用python实现,c语言实现部分使用libcli库做一个类似telnet的东东,回调函数run的时候调用python模块.针对c调用python,做个了小demo python模块:demo.py def print_arg(str): print str def add(a,b): print 'a=', a print 'b=', b return a + b class Class_A: def __init__(self): print "ini

PHP 调用Python脚本

上次做用户反馈自动翻译,写了个python脚本,将日文的用户反馈翻译成中文,效果虽然可以,但其它不懂python的童鞋就没法使用了,所以搭了个web服务,让其他人可以通过网页访问查询.使用的是apache服务,具体环境(LAMP)搭建就不细说,主要分享php调用python脚本后台运行的处理. 1. PHP如何调用外部程序 首先要解决的问题就是php如何调用python脚本,在PHP中调用外部程序主要有两个函数,system和exec. system()原型:string system(stri

c#中调用python

1. 安装IronPython,到http://ironpython.codeplex.com/下载安装包 2. 创建项目 创建一个C#的控制台应用程序. 添加引用: 浏览到IronPython的安装目录中,添加对IronPython.dll,Microsoft.Scripting.dll 两个dll的引用. 3. 添加Python文件到当前的项目中 创建一个文本文件命名为:hello.py, 编辑如下 def welcome(name):    return "hello" + na

C++调用python

本文以实例code讲解 C++ 调用 python 的方法. 本文在util.h中实现三个函数: 1. init_log: 用google log(glog)初始化log 2. exe_command: 由 C++ 执行 shell code 3. exe_py: C++调用python文件 Code: include/util.h: /*************************************************************************** *- *

QT中 使用c++调用python

最近在做一个项目,开发环境用的是QT c++.项目中使用amazon云服务,调研发现有一个Amazon云的python接口.就有了标题中的问题,需要用C++来调用python脚本. 下面是一个c++调用python的小例子.我先贴出代码,然后在详细介绍. # -*- coding: cp936 -*- #定义hello函数,这个函数的功能是输出“hello world!” def hello(): print("hello world!") 上面就是我们一会要用C++调用python脚

Crontab里调用python脚本不成功的解决方案

crontab里经常会调用一些程序,但是很多时候调用python是不成功的,但是手动则是OK的,在看日志(/var/log/cron)的时候,却是显示正常,这种情况怎么破呢?其实主要就是这么几个地方: 1)轻易不要用if __name__ == "__main__": 很多主管都习惯让自己手下人把脚本写成模块化,然后潇洒的走一个整合,但是crontab对这个方法似乎不是很灵光,因为python运行的时候是从if __name__ == "__main__"开始执行,