C++调用python

字数 6171阅读 4373

C++调用python

在C/C++中嵌入Python,可以使用Python提供的强大功能,通过嵌入Python可以替代动态链接库形式的接口,这样可以方便地根据需要修改脚本代码,而不用重新编译链接二进制的动态链接库。至少你可以把它当成文本形式的动态链接库,需要的时候还可以改一改,只要不改变接口, C++的程序一旦编译好了,再改就没那么方便了。

C++调用Python有两种方式

第一种方式:通过找到Python模块,类,方法,构造参数来调用。

第二中方式,就是通过构造出一个Python的脚本,用python引擎来执行。

第一种方式可能更为优雅,符合大多数的反射调用的特点。(如c#的反射机制,c#调用Com+,c#调用javascript脚本等)。

一个问题:两种语言互相调用的时候,需要做数据结构(如基本类型,字符串,整数类型等,以及自定义的类等类型)间的转换,共享内存中的一个对象。比如,如何将C++的对象实例传入python中,并在python中使用。c++和python并不在一个进程中,因此可以使用boost的shared_ptr来实现。Python调用C++,换句话说就是需要把C++封装成Python可以“理解”的类型。同理可知C++怎么去调用Python脚本。

下面这个例子,主要是演示了c++调用python,可以在c++中形成一个python脚本,然后利用PyRun_SimpleString调用;并且,构造一个c++的对象,传入到python中,并在python的脚本中调用其函数。

皮皮blog

VS中编译运行

vs安装配置

安装python3.4,然后配置系统环境变量。

安装Visual Studio2010(注意可以不用安装其它好多东西,只要安装c++就可以了)。[Visual Studio相关设置]

vs中新建一个win32控制台应用程序,一路确定完成。

VS2010的配置(设置编译环境)

1. c++调用python需要在vs2010中的cpp文件中加入,这个头文件在python安装目录Python\include下

要成功引入就要把Python.h的头文件目录(如D:\python3.4.2\include放在菜单 > 项目 > 属性 > C/C++ > 常规 > 附加包含目录下(或者右键项目)

2. 还需要一个python34.lib,如果不导入的话,会提示你出现这个文件的缺失。文件在python\libs下,找到此文件之后进入VS2010,菜单 >项目 > 属性 > 配置属性 > VC++目录 > 库目录,把刚才的绝对路径(如D:\python3.4.2\libs)添加进去,此时变成这样的了:D:\python3.4.2\libs;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib。这样就可以在VC程序中执行python文件了。

或者把D:\python3.4.2\libs放进项目 > 属性 > 配置属性 > 链接器 > 常规 > 附加库目录中。

Note: 上面的设置是对某个模式生效,如果将运行模式从debug改成了release,要再进去设置,否则设置不成功。

程序编辑

编辑c++代码

将PythonInvoke.cpp文件改成下面的代码,用于调用python程序helloworld

// PythonInvoke.cpp : 定义控制台应用程序的入口点。

#include "stdafx.h"

#include

void main(){

Py_Initialize(); /*初始化python解释器,告诉编译器要用的python编译器*/

PyRun_SimpleString("import helloworld"); /*调用python文件*/

PyRun_SimpleString("helloworld.printHello()");/*调用python文件中的函数*/

Py_Finalize(); /*结束python解释器,释放资源*/

system("pause");

}

Note: 当python代码有错误时,PyImport_ImportModule函数返回NULL;

另一种调用方式的代码

#include//前面所做的一切配置都是为了调用这个头文件和相关库

#include

using namespace std;

int main(){

Py_Initialize();//使用python之前,要调用Py_Initialize();这个函数进行初始化

PyObject * pModule = NULL;//声明变量

PyObject * pFunc = NULL;// 声明变量

pModule =PyImport_ImportModule("helloworld");//这里是要调用的文件名

pFunc= PyObject_GetAttrString(pModule, "Hello");//这里是要调用的函数名

PyEval_CallObject(pFunc, NULL);//调用函数

Py_Finalize();//调用Py_Finalize,这个根Py_Initialize相对应的。

return 0;

}

编辑python代码

在项目源文件中,添加文件命令为helloworld.py

def printHello():

print("Hello World!")

.py文件保存在.cpp同目录下

Note: 不能将python文件的名字命名为test.py,否则报错,由于test.py是python内置python脚本文件,也就是python有自己的test.py文件,并且其优先级比你的高。。

皮皮blog

程序运行

如果你安装的python是64位的,则vs2010中需要把解决方案平台定位‘X64’的模式下,否则配置不成功。

报错:fatal error LNK1112: 模块计算机类型“X86”与目标计算机类型“x64”冲突。[fatal error LNK1112]

Release模式下运行

release模式下运行不用设置太多东西

修改运行选项

Debug改为Release

win32下拉配置,新建x64,一路确定

注意,执行下面之前要在release模式下再设置一次VS2010的配置

运行

运行成功!

Debug模式下运行

1. Debug下,python/libs目录下的python34.lib需要复制并重命名为python34_d.lib的形式

设置:项目 > 属性 > 配置属性 > 链接器 > 输入 > 附加依赖库 > python34_d.lib。

[源码编译 python 生成 python26_d.lib ; 处理 error c101008d]

为什么会有python34.lib和python34_d.lib的差别就是因为:python_d.lib是 库的调试后形式,当我们以debug模式编译工程时,python就用这个lib文件,但是这个文件是不可用 的。对于这点,最快的办法就是强制要求python在任何情况下都是用非调试版本,就可以了。

就是说,不重命名的解决方法,对python头文件python/include/pyconfig.h进行修改:

#          if defined(_DEBUG)

#              pragma  comment(lib,"python34_d.lib")

#          elif defined(Py_LIMITED_API)

#              pragma comment(lib,"python3.lib")

#          else

#              pragma comment(lib,"python34.lib")

#          endif /* _DEBUG */

将DEBUG条件下的lib由python34_d.lib改为python34.lib.

#          if defined(_DEBUG)

#              pragma  comment(lib,"python34.lib")

2. 64位debug下的方案解决

右键项目名,点击属性,弹出项目属性页,找到链接器—高级,修改右侧的目标计算机,选择有X64的那个选项。如果没有,则选择编译器Configuration Manager中new,添加amd64等平台,然后工程属性中选择x64。

这一步好像也不用,只要在运行时选择x64就可以了。见3.运行。

Note: 属性 - 链接器 - 命令行 -附加选项:如果里面有"/MACHINE:I386"之类的,要删了。

3. 32位库改成64位库

项目 > 属性 > 配置属性 > Vc++目录> 库目录,这里要将32位库改成64位库,相当重要!

$(VCInstallDir)lib\amd64

$(VCInstallDir)atlmfc\lib\amd64

$(WindowsSdkDir)lib\x64

如:将D:\python3.4.2\libs;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib

换成D:\python3.4.2\libs;$(VCInstallDir)lib\amd64;$(VCInstallDir)atlmfc\lib\amd64;$(WindowsSdkDir)lib\x64;$(FrameworkSDKDir)\lib

没有这样设置会报错:msvcprtd.lib(MSVCP100D.dll) : fatal error LNK1112: module machine type ‘X86’ conflicts with target machine type ‘x64’

msvcprtd.lib(MSVCP100D.dll) : fatal error LNK1112: 模块计算机类型“X86”与目标计算机类型“x64”冲突

[msvcprtd.lib(MSVCP100D.dll) : fatal error LNK1112: module machine type ‘X86’ conflicts with target machine type ‘x64’]

[msvcprtd.lib(MSVCP100D.dll) : fatal error LNK1112: module machine type 'X86' conflicts with target machine type 'x64']

还有一个cmake类似的问题[fatal error LNK1112: 模块计算机类型“X86”与目标计算机类型“x64”冲突——我的解决方案]

3. 运行:右键项目名,选择清理解决方案,清理完成之后重新生成解决方案,然后选择X64平台编译器去debug,便可以调试成功。

可能存在的错误

c++调用python时报错LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

这个是由于日志文件引起的,可以将项目\属性\配置属性\清单工具\输入和输出\嵌入清单:原来是“是”,改成“否”。

或者将项目\属性\配置属性\链接器\清单文件\生成清单:原来是“是”,改成“否”。

如果仍然无效,判断是否已经安装了VS2012,如果已经安装,需要安装VS2010 sp1补丁。

[链接器工具错误 LNK1123]

但是如果程序要加写rc,必须带清单才能正常使用。所以这种治标不治本的方法失效了。

还有一种解决方案:

出现这个问题的原因:可能是因为系统最近多次更新,出现了两个版本的cvtres.exe。而系统变量里将这俩都引用了,编译的时候,不知道用哪个了,导致出错。所以要删掉一个。

一个在C:\Windows\Microsoft.NET\Framework\v4.0.30319\cvtres.exe,另一个在你安装VS的软件目录..\Microsoft Visual Studio 10.0\vc\bin\cvtres.exe

然后右键属性-->详细信息 查看两者版本号,把老的Kill掉,就完了。

[彻底解决 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏]

编译时_RTC_Shutdown和_RTC_InitBase相关错误的解决方法:

error LNK2001: 无法解析的外部符号 _RTC_Shutdown;error LNK2001: 无法解析的外部符号 _RTC_InitBase

右键点击项目,修改:属性 > 配置属性 > C/C++ > 代码生成 > 基本运行时检查,将值从“两者(......)”改为“默认值”。

[编译时_RTC_Shutdown和_RTC_InitBase相关错误的解决方法]

LINK : error LNK2001: 无法解析的外部符号 mainCRTStartup

其它错误

error LNK2019: 无法解析的外部符号 __imp_system,该符号在函数 main 中被引用

原因是system("pause");没有include

[C++调用python配置及编译出现的问题]

皮皮blog

命令行中直接调用执行

编译选项, 需要手动指定Python 的include 路径, 和链接接路径。

代码:

g++ Python.cpp -o Python-I/usr/include/python2.5 -L/usr/lib/python2.5-lpython2.5

皮皮blog

C++调用python带参数传递

调用Python函数时,参数的传递,就是c++的类型,怎么转换成Python的类型;另外一个问题是,Python函数的返回值,怎么转换成C++中的类型。

在C程序中用Python脚本传递参数,或者获得Python脚本的返回值,则要使用更多的函数来编写C程序。由于Python有自己的数据类型,因此在C程序中要使用专门的API对相应的数据类型进行操作。

常用的函数有以下几种

1.数字与字符串处理

在Python/C API中提供了Py_BuildValue()函数对数字和字符串进行转换处理,使之变成Python中相应的数据类型。其函数原型如下所示。

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

其参数含义如下。

·     format:格式化字符串,如表8-1所示。

Py_BuildValue()函数中剩余的参数即要转换的C语言中的整型、浮点型或者字符串等。其返回值为PyObject型的指针。在C语言中,所有的Python类型都被声明为PyObject型。

2.列表操作

在Python/C API中提供了PyList_New()函数用以创建一个新的Python列表。PyList_New()函数的返回值为所创建的列表。其函数原型如下所示。

PyObject* PyList_New( Py_ssize_t len)

其参数含义如下。

·     len:所创建列表的长度。

当列表创建以后,可以使用PyList_SetItem()函数向列表中添加项。其函数原型如下所示。

int PyList_SetItem( PyObject *list, Py_ssize_t index, PyObject *item)

其参数含义如下。

·     list:要添加项的列表。

·     index:所添加项的位置索引。

·     item:所添加项的值。

同样可以使用Python/C API中PyList_GetItem()函数来获取列表中某项的值。PyList_GetItem()函数返回项的值。其函数原型如下所示。

PyObject* PyList_GetItem( PyObject *list, Py_ssize_t index)

其参数含义如下。

·     list:要进行操作的列表。

·     index:项的位置索引。

Python/C API中提供了与Python中列表操作相对应的函数。例如列表的append方法对应于PyList_Append()函数。列表的sort方法对应于PyList_Sort()函数。列表的reverse方法对应于PyList_Reverse()函数。其函数原型分别如下所示。

int PyList_Append( PyObject *list, PyObject *item)

int PyList_Sort( PyObject *list)

int PyList_Reverse( PyObject *list)

对于PyList_Append()函数,其参数含义如下。

·     list:要进行操作的列表。

·     item:要参加的项。

对于PyList_Sort()和PyList_Reverse()函数,其参数含义相同。

·     list:要进行操作的列表。

3.元组操作

在Python/C API中提供了PyTuple_New()函数,用以创建一个新的Python元组。PyTuple_New()函数返回所创建的元组。其函数原型如下所示。

PyObject* PyTuple_New( Py_ssize_t len)

其参数含义如下。

·     len:所创建元组的长度。

当元组创建以后,可以使用PyTuple_SetItem()函数向元组中添加项。其函数原型如下所示。

int PyTuple_SetItem( PyObject *p, Py_ssize_t pos, PyObject *o)

其参数含义如下所示。

·     p:所进行操作的元组。

·     pos:所添加项的位置索引。

·     o:所添加的项值。

可以使用Python/C API中PyTuple_GetItem()函数来获取元组中某项的值。PyTuple_GetItem()函数返回项的值。其函数原型如下所示。

PyObject* PyTuple_GetItem( PyObject *p, Py_ssize_t pos)

其参数含义如下。

·     p:要进行操作的元组。

·     pos:项的位置索引。

当元组创建以后可以使用_PyTuple_Resize()函数重新调整元组的大小。其函数原型如下所示。

int _PyTuple_Resize( PyObject **p, Py_ssize_t newsize)

其参数含义如下。

·     p:指向要进行操作的元组的指针。

·     newsize:新元组的大小。

4.字典操作

在Python/C API中提供了PyDict_New()函数用以创建一个新的字典。PyDict_New()函数返回所创建的字典。其函数原型如下所示。

PyObject* PyDict_New()

当字典创建后,可以使用PyDict_SetItem()函数和PyDict_SetItemString()函数向字典中添加项。其函数原型分别如下所示。

int PyDict_SetItem( PyObject *p, PyObject *key, PyObject *val)

int PyDict_SetItemString( PyObject *p, const char *key, PyObject *val)

其参数含义如下。

·     p:要进行操作的字典。

·     key:添加项的关键字,对于PyDict_SetItem()函数其为PyObject型,对于PyDict_SetItemString()函数其为char型。

·     val:添加项的值。

使用Python/C API中的PyDict_GetItem()函数和PyDict_GetItemString()函数来获取字典中某项的值。它们都返回项的值。其函数原型分别如下所示。

PyObject* PyDict_GetItem( PyObject *p, PyObject *key)

PyObject* PyDict_GetItemString( PyObject *p, const char *key)

其参数含义如下。

·     p:要进行操作的字典。

·     key:添加项的关键字,对于PyDict_GetItem()函数其为PyObject型,对于PyDict_GetItemString()函数其为char型。

使用Python/C API中的PyDict_DelItem()函数和PyDict_DelItemString()函数可以删除字典中的某一项。其函数原型如下所示。

int PyDict_DelItem( PyObject *p, PyObject *key)

int PyDict_DelItemString( PyObject *p, char *key)

其参数含义如下。

·     p:要进行操作的字典。

·     key:添加项的关键字,对于PyDict_DelItem()函数其为PyObject型,对于PyDict_DelItemString()函数其为char型。

使用Python/C API中的PyDict_Next()函数可以对字典进行遍历。其函数原型如下所示。

int PyDict_Next( PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)

其参数含义如下。

·     p:要进行遍历的字典。

·     ppos:字典中项的位置,应该被初始化为0。

·     pkey:返回字典的关键字。

·     pvalue:返回字典的值。

在Python/C API中提供了与Python中字典操作相对应的函数。例如字典的item方法对应于PyDict_Items()函数。字典的keys方法对应于PyDict_Keys()函数。字典的values方法对应于PyDict_Values()函数。其函数原型分别如下所示。

PyObject* PyDict_Items( PyObject *p)

PyObject* PyDict_Keys( PyObject *p)

PyObject* PyDict_Values( PyObject *p)

其参数含义如下。

·     p:要进行操作的字典。

5.释放资源

Python使用引用计数机制对内存进行管理,实现自动垃圾回收。在C/C++中使用Python对象时,应正确地处理引用计数,否则容易导致内存泄漏。在Python/C API中提供了Py_CLEAR()、Py_DECREF()等宏来对引用计数进行操作。

当使用Python/C API中的函数创建列表、元组、字典等后,就在内存中生成了这些对象的引用计数。在对其完成操作后应该使用Py_CLEAR()、Py_DECREF()等宏来销毁这些对象。其原型分别如下所示。

void Py_CLEAR( PyObject *o)

void Py_DECREF( PyObject *o)

其参数含义如下。

·     o:要进行操作的对象。

对于Py_CLEAR()其参数可以为NULL指针,此时,Py_CLEAR()不进行任何操作。而对于Py_DECREF()其参数不能为NULL指针,否则将导致错误。

6.模块与函数

使用Python/C API中的PyImport_Import()函数可以在C程序中导入Python模块。PyImport_Import()函数返回一个模块对象。其函数原型如下所示。

PyObject* PyImport_Import( PyObject *name)

其参数含义如下。

·     name:要导入的模块名。

使用Python/C API中的PyObject_CallObject()函数和PyObject_CallFunction()函数,可以在C程序中调用Python中的函数。其参数原型分别如下所示。

PyObject* PyObject_CallObject( PyObject *callable_object, PyObject *args)

PyObject* PyObject_CallFunction( PyObject *callable, char *format, ...)

对于PyObject_CallObject()函数,其参数含义如下。

·     callable_object:要调用的函数对象。

·     args:元组形式的参数列表。

对于PyObject_CallFunction()函数,其参数含义如下。

·     callable_object:要调用的函数对象。

·     format:指定参数的类型。

·     ...:向函数传递的参数。

使用Python/C API中的PyModule_GetDict()函数可以获得Python模块中的函数列表。PyModule_GetDict()函数返回一个字典。字典中的关键字为函数名,值为函数的调用地址。其函数原型如下所示。

PyObject* PyModule_GetDict( PyObject *module)

其参数含义如下。

·     module:已导入的模块对象。

8.2.3 在C中嵌入Python实例

在VC++ 6.0中新建一个名为“EmbPython”的空“Win32 Console Application”工程。向其添加如下所示的“EmbPython.c”文件。

程序输出如下所示。

-==在C中嵌入Python==-

使用Python中的sum函数求解下列数之和

0       1       2       3       4

Using Function sum

The result is: 10

使用Python中的函数分割以下字符串:

this is an example

结果如下所示:

this

is

an

example

按回车键退出程序

[Python嵌入C/C++ (Python核心编程)]

C++转换成Python类型,Py_BuildValue()

http://www.python.org/doc/1.5.2p2/ext/buildValue.html

PyObject* pArgs=PyTuple_New(1); //有几个参数,就是几

PyTuple_SetItem(pArgs,0,Py_BuildValue("i",3));  //初始第一个参数,数据类型是i,就是int,值是3

返回值转换如,PyArg_ParseTuple[PyArg_ParseTuple]

皮皮blog

python调用c++问题

“如果没有参数从python到C++, 是正常的,但是有参数就废了报错”。我尝试实现并找到答案。

http://stackoverflow.com/questions/145270/calling-c-c-from-python

1. 无参数 函数声明C可用函数

2. 有参数 那么实用SWIG  也就是我们需要一个接口文件 即

z.i file

%{

#include "z.h"

extern 函数名(参数1, 参数2,...);

%}

SWIG在不同语言互相调用发挥很重要的作用。

相关话题

SWIG

有一个外部工具叫SWIG,是Simplified Wrapper and Interface Generator 的缩写。其作者为David Beazley,同时也是Python Essential Referenc 一书的作者。这个工具可以根据特别注释过的C/C++头文件生成能给Python,Tcl 和Perl 使用的包装代码。使用SWIG 可以省去你写前面所说的样板代码的时间。你只要关心怎么用C/C++解决你的实际问题就好了。你所要做的就是按SWIG 的格式编写文件,其余的就都由SWIG 来完成。你可以通过下面的网址找到关于SWIG 的更多信息。

http://swig.org

Pyrex

创建C/C++扩展的一个很明显的坏处是你必须要写C/C++代码。你能利用它们的优点,但更重要的是,你也会碰到它们的缺点。Pyrex 可以让你只取扩展的优点,而完全没有后顾之忧。它是一种更偏向Python 的C 语言和Python 语言的混合语言。事实上,Pyrex 的官方网站上就说“Pyrex 是具有C 数据类型的Python“。你只要用Pyrex 的语法写代码,然后运行Pyrex 编译器去编译源代码。Pyrex会生成相应的C 代码,这些代码可以被编译成普通的扩展。你可以在它的官方网站下载到Pyrex:

http://cosc.canterbury.ac.nz/~greg/python/Pyrex

Psyco

Pyrex 免去了我们再去写纯C 代码的麻烦。不过,你要去学会它的那一套与众不同的语法。最后,你的Pyrex 代码还是会被转成C 的代码。无论你用C/C++,C/C++加上SWIG,还是Pyrex,都是因为你想要加快你的程序的速度。如果你可以在不改动你的Python 代码的同时,又能获得速度的提升,那该多好啊。

Psyco 的理念与其它的方法截然不同。与其改成C 的代码,为何不让你已有的Python 代码

运行的更快一些呢?Psyco 是一个just-in-time(JIT)编译器,它能在运行时自动把字节码转为本地代码运行。所以,

你只要(在运行时)导入Psyco 模块,然后告诉它要开始优化代码就可以了。而不用修改自己的代

码。Psyco 也可以检查你代码各个部分的运行时间,以找出瓶颈所在。你甚至可以打开日志功能,来

查看Psyco 在优化你的代码的时候,都做了些什么。你可以访问以下网站获取更多的信息:

http://psyco.sf.net

嵌入

嵌入是Python 的另一功能。与把C 代码包装到Python 中的扩展相对的,嵌入是把Python 解释器包装到C 的程序中。这样做可以给大型的,单一的,要求严格的,私有的并且(或者)极其重要的应用程序内嵌Python 解释器的能力。一旦内嵌了Python,世界完全不一样了。

Python 提供了很多官方文档供写扩展的人参考:

扩展与嵌入

http://docs.python.org/ext

Python/C API

http://docs.python.org/api

分发Python 模块

http://docs.python.org/dist

[python核心编程2e.d. - 扩展Python]

from:http://blog.csdn.net/pipisorry/article/details/49532341

ref:python doc:Python/C API Reference Manual

python doc:EmbeddingPython in Another Application

C++中调用Python脚本

C++调用Python

C++调Python示例

在VS2012中C++嵌入python

[如何实现 C/C++ 与 Python 的通信?]*

[Google开发了一个框架让C++和Python交互更容易]让开发者为C++程序生成Python的接口包装器

推荐阅读更多精彩内容