使用解释器
到现在为止,您应该知道如何使用Boost.Python从Python调用C ++代码。 但是,有时您可能需要反过来:从C ++端调用Python代码。 这需要您将 Python解释器嵌入到C ++程序中。
目前,Boost.Python并不直接支持嵌入时您需要的所有内容。 因此,您需要使用Python / C API填补空白。 但是,Boost.Python已经使嵌入变得更加容易,并且在未来的版本中,根本不需要触摸Python / C API。 敬请期待...
构建嵌入式程序
为了能够将python嵌入到程序中,您必须链接到Boost.Python以及Python自己的运行时库。
Boost.Python的库有两种变体。 两者都位于Boost的/libs/python/build/bin-stage
子目录中。 在Windows上,变体称为boost_python.lib
(用于发布版本)和boost_python_debug.lib
(用于调试)。 如果找不到库,可能还没有构建Boost.Python。
Python的库可以在Python目录的/libs
子目录中找到。 在Windows上,它被称为pythonXY.lib,其中XY是您的主要Python版本号。
此外,必须将Python的/include
子目录添加到包含路径中。
在Jamfile中,以上所有内容归结为:
projectroot c:\projects\embedded_program ; # location of the program
# bring in the rules for python
SEARCH on python.jam = $(BOOST_BUILD_PATH) ;
include python.jam ;
exe embedded_program # name of the executable
: #sources
embedded_program.cpp
: # requirements
<find-library>boost_python <library-path>c:\boost\libs\python
$(PYTHON_PROPERTIES)
<library-path>$(PYTHON_LIB_PATH)
<find-library>$(PYTHON_EMBEDDED_LIBRARY) ;
入门
能够构建很好,但还没有什么可以构建。 将Python解释器嵌入到您的一个C ++程序中需要以下4个步骤:
- #include
<boost/python.hpp>
- 调用Py_Initialize ()来启动解释器并创建
__main__
模块。 - 调用其他Python C API例程来使用解释器。
![]() | 注意 |
---|---|
请注意,此时您不能调用Py_Finalize ()来停止解释器。 这可以在boost.python的未来版本中修复。 |
(当然,在所有这些步骤之间可以有其他C ++代码。)
现在我们可以将解释器嵌入到我们的程序中,让我们看看如何使用它...
使用解释器
您可能已经知道,Python中的对象是引用计数的。 当然,Python C API的PyObject
也是引用计数的。 但是有一点不同。 虽然引用计数在Python中是完全自动的,但Python C API要求您手动完成 。 这很麻烦,特别是在C ++异常存在的情况下很难做到。 幸运的是,Boost.Python提供了句柄和对象类模板来自动化该过程。
运行Python代码
Boost.python提供了三个相关的函数来从C ++运行Python代码。
object eval(str expression, object globals = object(), object locals = object())
object exec(str code, object globals = object(), object locals = object())
object exec_file(str filename, object globals = object(), object locals = object())
eval计算给定的表达式并返回结果值。 exec执行返回结果的给定代码(通常是一组语句),exec_file执行给定文件中包含的代码。
globals
和locals
参数是Python字典,包含运行代码的上下文的全局变量和局部变量。 对于大多数意图和目的,您可以使用__main__
模块的命名空间字典作为两个参数。
Boost.python提供了导入模块的功能:
object import(str name)
import导入python模块(可能首先将其加载到正在运行的进程中),然后返回它。
让我们导入__main__
模块并在其命名空间中运行一些Python代码:
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
object ignored = exec("hello = file('hello.txt', 'w')\n"
"hello.write('Hello world!')\n"
"hello.close()",
main_namespace);
这应该在当前目录中创建一个名为“hello.txt”的文件,其中包含编程圈中众所周知的短语。
操纵Python对象
通常我们想要一个类来操作Python对象。 但是我们已经在上面看过这样一个类,在上一节中 :恰当命名的object
类及其派生类。 我们已经看到它们可以用handle
构造。 以下示例应进一步说明这一事实:
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
object ignored = exec("result = 5 ** 2", main_namespace);
int five_squared = extract<int>(main_namespace["result"]);
这里我们为__main__
模块的命名空间创建一个字典对象。 然后我们将5平方分配给结果变量并从字典中读取该变量。 另一种实现相同结果的方法是使用eval代替,它直接返回结果:
object result = eval("5 ** 2");
int five_squared = extract<int>(result);
异常处理
如果在评估python表达式时发生异常,则抛出error_already_set :
try
{
object result = eval("5/0");
// execution will never get here:
int five_divided_by_zero = extract<int>(result);
}
catch(error_already_set const &)
{
// handle the exception in some way
}
error_already_set
异常类本身不携带任何信息。 要了解有关发生的Python异常的更多信息,您需要在catch语句中使用Python C API的异常处理函数 。 这可以像调用PyErr_Print()将异常的回溯打印到控制台,或者将异常的类型与标准异常的类型进行比较一样简单:
catch(error_already_set const &)
{
if (PyErr_ExceptionMatches(PyExc_ZeroDivisionError))
{
// handle ZeroDivisionError specially
}
else
{
// print all other errors to stderr
PyErr_Print();
}
}