使用Boost::Python在C++应用程序中嵌入Python:第一部分

翻译: Leon Lee([email protected])
原文:在此

在本系列教程的简介中,我说了将Python代码集成到Granola代码库中的动机。简而言之,它可以使我使用Python语言和标准库的好处来完成在C++中通常很痛苦或笨拙的任务。当然,底线是我不必移植任何已有的C++代码。

今天,我们看一下使用boost::python在C++中嵌入Python并与Python对象交互的基本步骤。我已将此部分中的所有代码放在github仓库中,请随意检出代码并使用。

从Python的内核来说,嵌入Python非常简单,不需要任何C++代码--Python发行版提供的库中包括C绑定内容。我们将跳过所有这些,直接进入通过boost::python在C++中使用Python,它提供了类包装和多态行为,相比C绑定,更与实际Python代码一致、本教程后面的部分,我们将介绍一些无法通过boost::python做到的事情(特别是多线程和错误处理)

好了,要开始的话,首先需要下载并构建boost,或者在包管理器得到一份副本。如果你选择构建,你可以只构建boost::python库(可惜不只是头文件),但是如果你经常使用C++编程,我还是建议熟悉整个boost库。如果你已经同步了上面的git仓库,确保在Makefile里把路径指向你的boost安装目录。好了,我们继续。

首先,我们需要能够构建嵌入Python的应用程序。使用gcc这不是很困难,它只是将boost::python和libpython以静态或者共享库的方式包含进来。根据你构建boost的方式不同,你可能会遇到各种困难。在github上的教程代码里,我们使用静态的boost::python库(libboost_python.a)和Python库的动态版本(libpython.so)。

我在MiserWare的开发工作的一个软性要求是使我们所有支持操作系统(一些Windows和一系列不断变化的Linux发行版)的环境保持一致。因此Granola链接到固定的Python版本,安装的版本里包括了运行代码所需要的Python库文件。也许并不理想,但是它提供了一个我肯定我们的代码将在所有支持的操作系统上运行的环境。

让我们运行一些代码。可以想象,可能需要包含正确的头文件。

Py_Initialize();     
py::object main_module = py::import(“__ main__”);     
py::object main_namespace = main_module.attr(“__ dict__”);   
 

注意,你必须直接初始化Python解释器(第一行)。虽然boost::python极大的简化了嵌入Python的任务,但是它并不能处理你需要做的所有事情。正如前面提到的,我们将在接下来的教程里看到更多的缺陷。在初始化以后,__main__模块被导入,命名空间被解析,这将产生空白的运行环境,我们可以在上面调用Python代码,添加模块和变量。

boost::python::exec("print 'Hello, world'", main_namespace);
boost::python::exec("print 'Hello, world'[3:5]", main_namespace);
boost::python::exec("print '.'.join(['1','2','3'])", main_namespace);

exec函数在指定的命名空间内运行字符串参数中的代码。所有正常的、未导入的代码都可以。当然,由于不能导入模块和提取值,因此不是很有用。

boost::python::exec("import random", main_namespace);
boost::python::object rand = boost::python::eval("random.random()", main_namespace);
std::cout << py::extract(rand) << std::endl;

这里我们在命名空间__main__里通过执行相应的Python语句来导入random模块,把这个模块带入这个命名空间。当模块可用后,我们可以在这个命名空间里使用函数、对象和变量。本例里,我们使用了eval函数,它返回传入的Python语句的运行结果,来创建一个boost::python对象来包含random模块的random()函数返回的随机值。最后,我们将值以C++ double类型提取并打印出来。

这可能看上去有点......软。通过将格式化的Python字符串传递给C++函数来调用Python?这不是以一种非常面向对象的方式来处理事务。幸运的是,有一种更好的办法。

boost::python::object rand_mod = boost::python::import("random");
boost::python::object rand_func = rand_mod.attr("random");
boost::python::object rand2 = rand_func();
std::cout << boost::python::extract(rand2) << std::endl;

在这个最后的例子里,我们导入了random模块,但这次我们使用的是boost::python的import函数,它把模块加载到boost python的对象中。接下来,random函数对象从random模块中提取出来并存储在boost::python对象中。调用该函数,返回一个包含随机数的Python对象。最后double值被提取和打印出来。通常,所有Python对象都可以以这种方式处理--函数、类、内置类型。

当你开始持有复杂的标准库对象和用户定义类的实例时,它开始变得有趣。接下来的教程,我将按部就班围绕ConfigParser模块构建一个真正的配置解析类讨论从C++代码解析Python异常。

你可能感兴趣的:(编程)