c++与python的互相调用

【编者按】最近一直发Python的资料,是因为Python的脚本扩展性。现在.net的动态语言特性已经很强大了,似乎脚本的作用并不明显。但是对于老式的C++,如果能够结合脚本语言的动态性,引用最近流行的一句话:必可以形成犄角之势!C++调用Python可以通过API,也可以通过Boost库实现,Boost.Python就是对API的包装,方便调用而已。

这是一篇比较老的介绍Boost.Python的文章,权当入门吧。因为文中一些东西现在不对……

Boost.Python是 Boost 中的一个组件, 使用它能够大大简化用 C++ 为 Python 写扩展库的步骤, 提高开发效率, 虽然目前它对 Python 嵌入 C++ 的支持还不是很多, 但也能提供很大方便。 另外, 华宇煜 也编写了一份关于 Boost.Python 简明教程 。

1 Boost 安装简介

在正式开始使用 Boost.Python 之前, 我们必须先编译 Boost。 首先到 Boost 的官方站点 下载 Boost 的源码包, 把它们解压到你喜欢的目录, 为编译做好准备。 另外, 在正式安装 Boost.Python 之前, 我们必须先正确安装 Python。

1.1 Linux 下的编译

首先切换到 Boost 源码所在的路径, 执行 ./configure 脚本, 为配置脚本提供 Python 运行环境相应的参数:

./configure --with-python=/usr/bin/python / 
            --with-python-version=2.4 / 
            --with-python-root=/usr

然后, 和绝大部分 Linux 程序一行, 执行 make 就可以开始编译了。 编译完毕后, 切换到 root 权限后再执行 make install , 把 Boost 相应的头文件和库文件复制到相应的地方, 就可以使用了。

1.2 使用 MinGW + MSys 在 Windows 下的编译

首先需要编译的是 Boost 的编译工具 bjam, 直接到 bjam 所在目录下, 即 Boost 源码包所在目录下的 /tools/build/jam_src , 执行build.bat mingw , 稍等片刻, bjam.exe 就编译好了。 把编译好的 bjam.exe 复制到你的 %PATH% 路径能够直接找到的地方, 为后续的编译工作做好准备。

接下来, 切换到 Boost 源码所在路径, 执行 bjam 进行编译。 我们需要提供关于 Python 的一些参数, 变量 PYTHON_ROOT 指向 Python 运行环境所在的目录, 变量 PYTHON_VERSION 的值为 Python 的版本号, 如果你的 Python 安装路径与滇狐不同, 请将相应的变量修改为你机器上相应的路径, 编译命令行如下:

bjam.exe "-sTOOLS=mingw" "-sPYTHON_ROOT=E:/Python" "-sPYTHON_VERSION=2.4"

编译完毕后, 你将会在你的 C:/Boost 下找到编译得到的 Boost 相应头文件与库文件, 你可以根据你的需要将它移动到别的地方备用。

2 使用 Boost.Python 嵌入 Python 模块到 C++

Boost.Python 目前并没有提供完整的将 Python 模块嵌入到 C++ 的包装库, 因此许多工作我们还必须通过 Python C API 来进行。 但是, 利用 Boost.Python 中提供的一些模块, 能够给我们的工作带来极大便利。

2.1 修改模块加载路径,装入 Python 模块

与任何一个其它 Python 嵌入 C/C++ 的程序一样, 我们需要在第一条 #include 语句处含入 Python.h , 并在程序开始时调用 Py_Initialize(), 在程序结束时调用 Py_Finalize() 。

接下来, 我们便可以开始准备装入 Python 模块了。 为了让 Python 解释器能够正确地找到 Python 模块所在的位置, 我们需要将 Python 模块所在的路径添加到模块搜索路径中, 添加搜索路径的 Python 语句如下:

import sys 
if  not ‘/module/path ‘ in sys.path: 
    sys.path.append(‘/module/path ‘)

我们使用 Python C API 执行类似的语句, 就能将模块的搜索路径添加到 Python 解释器中。 添加了搜索路径后, 就可以通过PyImport_ImportModule 函数加载 Python 模块了。 PyImport_ImportModule 返回值是 PyObject * , 为了避免手工处理繁琐的引用计数等问题, 我们求助于 Boost.Python 提供的 handle 模块, 将 PyObject * 封装起来, 以方便使用, 代码如下:

#include

...

boost::python::handle<>* _module; // Module handle. 
    std::string path; // Path of the Python module. 
    std::string module; // Module name.

...

try 
    { 
        PyRun_SimpleString("import sys" ); 
        PyRun_SimpleString((std::string("if not ‘" ) + path 
            + "‘ in sys.path: sys.path.append(‘" + path + "‘)" ).c_str()); 
        _module = new boost::python::handle<>( 
            PyImport_ImportModule((char *) module)); 
        ... 
    } 
    catch (...) 
    { 
        PyErr_Print(); 
        PyErr_Clear(); 
        delete _module; 
        _module = NULL ; 
        return  false ; 
    }

...

需要注意的是, 通过 Python C API 加载的 Python 解释器并没有把当前路径列入默认的搜索路径中。 因此, 即使你的 Python 模块就存放在当前路径, 你也必须使用上面的代码将当前路径添加到搜索路径中之后, 才能通过 PyImport_ImportModule 加载到模块。

当 Python 模块使用完毕或程序结束时, 请使用 delete 将 _module 指针释放, handle 被释放的时候会自动释放相应的 Python 模块并回收相应资源。

2.2 调用 Python 函数

导入了 Python 模块之后, 调用 Python 函数就非常容易了。 Boost.Python 里封装了一个非常好用的模板函数 boost::python::call_method, 它可以替你处理调用函数时需要处理的种种细节, 将你从 Python C API 中繁琐的“将参数打包为 PyObject * ”、 “构造 Tuple”、 “传递 Tuple”、 “解包返回值”等工作中彻底解放出来, 你只需要这样:

boost::python::call_method<返回值类型>(模块指针, "Python 函数名" , 
        参数 1 , 参数 2 , ...);

模块指针可以通过我们前面得到的 _module 的 get 方法获得, 例如:

... 
    bool result; 
    std::string config_file;

...

try 
    { 
        return boost::python::call_method<bool >(_module->get(), "initialize" , 
            config_file); 
    } 
    catch (...) 
    { 
        PyErr_Print(); 
        PyErr_Clear(); 
        ... 
    }

...

2.3 使用 Python 类对象

使用 Python C API 调用 Python 函数和调用 Python 类对象是没有太大区别的, 我们只需要调用类的构造方法, 得到一个类对象, 然后把该类的指针看做模块指针, 按照前面调用普通函数的方法调用类成员方法就可以了。 例如, 下列代码从 _module 中创建了一个YukiSession 对象, 然后调用了其中的 on_welcome 方法。 除了展示调用类成员方法外, 这段代码还展示了构造 Python list 对象、 从 Python list 对象中获取元素的方式。

...

boost::python::handle<> _yukisession;

...

// Retrieve the module handle and namespace handle. 
    boost::python::object main_module(*_module); 
    boost::python::object main_namespace = main_module.attr("__dict__" );

// Call the method and get the object handle. 
    _yukisession = boost::python::handle<>((PyRun_String( 
        "YukiSession()" , Py_eval_input, 
        main_namespace.ptr(), main_namespace.ptr()))); 
    ...

// Compose a list. 
    boost::python::list param; 
    param.append(boost::python::str(_addr.get_host_addr())); 
    param.append(boost::python::str());

// Call the method and retrieve the result. 
    // Method is equivalent to: 
    // "bool __thiscall YukiSession::on_welcome(list param);" 
    result = boost::python::call_method<bool > 
        (_yukisession.get(), "on_welcome" , param); 
    // Extract an item from a list. 
    str = boost::python::call_method 
        (param.ptr(), "__getitem__" , 1 );

...

3 在嵌入的 Python 模块中调用 C++ 程序

通过动态链接库的方式使用 Boost.Python 导出 C++ 模块到 Python 程序与在 C++ 可执行程序中导出模块给嵌入的 Python 解释器, 编写程序的方式几乎是完全相同的。 因此这里只简单介绍导出普通函数的方法, 想详细了解更多高级功能, 如导出 C++ 类、 导出可被 Python 重载的类等, 可以参看华宇煜的 Boost.Python 简明教程 或官方 Boost.Python 文档。

3.1 导出 C++ 函数

首先使用 BOOST_PYTHON_MODULE 宏定义需要导出给 Python 的模块, 然后用 boost::python::def 语句定义导出的函数、 参数列表以及 Doc String, 例如在下面的例子中, 我们导出了一个 C++ 函数 yukigettext , 并重命名为 gettext :

const  char *yukigettext(const  char *id);

BOOST_PYTHON_MODULE(yuki) 

    boost::python::def("gettext" , yukigettext, 
        boost::python::args("id" ), "Translate message." ); 
}

3.2 为 Python 初始化 C++ 模块

使用 BOOST_PYTHON_MODULE(name) 定义了 Python 模块后, 该宏会自动生成一个函数 initname , 我们需要在 Py_Initialize() 之后调用这个自动生成的函数, 初始化导出到 Python 的模块。 例如我们刚才导出模块用的宏 BOOST_PYTHON_MODULE(yuki) , 因此初始化的时候就应该调用 inityuki() :

... 
    Py_Initialize(); 
    inityuki(); 
...

3.3 在 Python 模块中调用 C++ 模块

此时我们在 Python 模块中只需要像普通的 Python 模块那样, 将导入的 C++ 模块用 import 语句加载进来, 就可以调用了:

import yuki

...

print yuki.gettext("This is a test! ")

原文:http://edyfox.codecarver.org/html/boost_python.html

时间: 2024-08-29 09:38:39

c++与python的互相调用的相关文章

linux 下的动态库制作 以及在python 中如何调用 c 函数库

动态库: 动态库又称动态链接库英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件.动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数.函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译.链接并与使用它们的进程分开存储的函数.DLL 还有助于共享数据和资源.多个应用程序可同时访问内存中单个DLL 副本的内容.DLL 是一个包含可由多个程序同时使用的代码和数据的库.Wind

python可变参数调用函数问题

一直使用python实现一些想法,最近在使用python的过程中出现这样一个需求,定义了一个函数,第一个是普通参数,第二个是默认参数,后面还有可变参数,在最初学习python的时候,都知道非关键字可变参数和关键字可变参数两种,调用的方式也非常多种多样,这里主要提出一个比较隐含的问题,并将各种可能出现的情况进行了探讨. 函数声明格式 python虽然不支持函数重载,但是通过对函数参数的众多特性的支持基本弥补了.函数声明的通式如下: def func(argv1,argv2...[,argv_d =

python网络爬虫入门(二)——用python简单实现调用谷歌翻译

最近在看国外的文档,有些生词不认识.就用谷歌翻译来理解,用着用着闲来无事就按F12查看了下页面的源代码.发现可以用python简单的实现下谷歌翻译的页面功能.于是先上网搜下有没有类似的文章博客,发现几篇不错的,于是参考其他代码与自己的思路,简单的实现了下翻译的功能,代码如下: import re import urllib,urllib2 #----------模拟浏览器的行为,向谷歌翻译发送数据,然后抓取翻译结果,这就是大概的思路------- def Gtranslate(text): #t

python发布及调用基于SOAP的webservice

现如今面向服务(SOA)的架构设计已经成为主流,把公用的服务打包成一个个webservice供各方调用是一种非常常用的做法,而应用最广泛的则是基于SOAP协议和wsdl的webservice.本文讲解python环境下如何发布及调用一个基于SOAP的webservice,基于soaplib(发布)和suds(调用). OS:ubuntu 14.04  python:2.7.6 服务端: 1.安装: 服务端要使用的工具包是soaplib,遗憾的是现在也停止维护了,不过好在还能用,下载地址在http

`python`被`C# `调用的方法

目的: 熟悉markdown 记录python被C#调用的方法 参考材料: markdown语法 python被C#调用的方法(参考自博客内容) 调用方法: 采用Ironpython: 优点: 快捷方便,方便传递参数等,因为相当于是将python代码传递进C#之中进行操作,语法逻辑比较清楚. 缺点: 需要额外配置Ironpython, 且不支持python3.X.不共享系统中安装的python环境包,需要额外配置路径或者重装numpy之类的软件包. 示例代码: using IronPython.

python cmd命令调用

关于python调用cmd命令: 主要介绍两种方式: 1.python的OS模块. OS模块调用CMD命令有两种方式:os.popen(),os.system(). 都是用当前进程来调用. os.system是无法获取返回值的.当运行结束后接着往下面执行程序.用法如:OS.system("ipconfig"). OS.popen带返回值的,如何获取返回值.如 p=os.popen(cmd) print p.read().得到的是个字符串. 这两个都是用当前进程来调用,也就是说它们都是阻

Python 3.X 调用多线程C模块,并在C模块中回调python函数的示例

由于最近在做一个C++面向Python的API封装项目,因此需要用到C扩展Python的相关知识.在此进行简要的总结. 此篇示例分为三部分.第一部分展示了如何用C在Windows中进行多线程编程:第二部分将第一部分的示例进行扩展,展示了如何在python中调用多线程的C模块:第三部分扩展了第二部分,增加了在C模块的线程中回调python的演示. 本文所用的环境为:64位Win7 + python 3.4 x86 + vs2010 一.windows下的C语言多线程程序 windows下多线程编程

python学习日常-调用函数&amp;定义函数

这几天由于生病,所以停了下. 调用函数相当的简单,比其他许多语言简单多了.我们知道,python中有许多自带的方法,我们要用的时候,就直接“方法名(参数);"就可以调用了.比如调用类型转换方法将一个int类型转换为str类型就可以str(123):就好了 定义函数,一个函数简单的定义就是 关键字(def) 方法名(参数): 做什么: 比如定义一个a+b的方法 def Add(a,b): return a+b; 忘了说明,python函数一定有返回值,即return,你可以返回为none,但是还是

利用thrift在c++、java和python之间相互调用

转自:http://blog.csdn.net/andy_yf/article/details/7487384 thrift做为跨语言调用的方案有高效,支持语言较多,成熟等优点:代码侵入较强是其弱点. 下面记录以C++做服务器,C++,java和python做客户端的示例,这个和本人现在工作环境吻合,使用多线程长连接的socket来建立高效分布式系统的跨语言调用平台.遗憾的是目前版本(0.7.0)的C语言还不支持Compact协议,导致在现在的环境中nginx c module调用thrift要