赞
踩
目录
Boost.Python 不仅可以提供将 C++ 代码封装成 Python 接口供 Python 程序调用的功能,反过来,也可以使用 Boost.Python 在 C++ 程序中调用 Python 方法。但是需要注意的是,截至当前版本的 Boost(1.79) 的这一功能还不完善,需要与 Python/C API 结合使用。官方说法是后续可能会完善,但是当前还不能独立使用。
先总结一下个人想法吧,总体看下来,Boost.Python 的嵌入 Python 方案其实现完全依靠调用 Python/C API 的内容,只是在后者基础之上做了一层封装,使得看起来更加便利了而已。对于开发者来说,如果仅仅为了实现嵌入 Python 的功能,没有必要使用 Boost 库,但是对于依赖 Boost 的软件项目来说,使用 Boost.Python 确实能简化不少流程,且能降低这方面出错的概率。
Boost.Python 包含两种模型 extending 和 embedding。前者是将 C++ 编写的库封装成 Python 接口时使用的,后者则用于在 C++ 程序中调用 Python 解释器作为库子例程,一般为现有应用程序添加脚本功能。两者的主要区别在于 C++ main() 函数的位置,分别在 Python 解释器可执行文件或其他程序中。
我们将在 C/C++ 代码调用的 Python 语句称为 Embedding Python。
注:关于 extending 和 embedding,我们不必太在意细节,对于软件开发人员来说,他们在使用上没有任何区别。通常能使用 Boost.Python 库参与编译,这两者都是可用的。
使用 Boost.Python 编写嵌入 Python 程序一般遵循以下4个步骤:
注:在当前为止的版本(1.79),我们需要手动调用 Py_Finalize() 来停止 Python 解释器,在将来的版本中会解决这个问题,但是在当前还是需要。
先看一个简单的示例:
- #include <boost/python.hpp>
-
- using namespace boost::python;
-
- int main( int argc, char ** argv ) {
- try {
- Py_Initialize();
-
- // 方法一: 使用 Python C API 打印
- /*
- object main_module((
- handle<>(borrowed(PyImport_AddModule("__main__")))));
- object main_namespace = main_module.attr("__dict__");
- handle<> ignored(( PyRun_String( "print(\"Hello, World\")",
- Py_file_input,
- main_namespace.ptr(),
- main_namespace.ptr() ) ));
- */
-
- // 方法二:使用 Boost.Python 接口打印
- object ignored = exec("print(\"Hello, World\")");
-
- Py_Finalize();
- } catch( error_already_set ) {
- PyErr_Print();
- }
- }
CMakeLists.txt
- set(MODULE_NAME embedding)
-
- add_executable(${MODULE_NAME} hello.cpp)
-
- target_link_libraries(${MODULE_NAME}
- ${Boost_LIBRARIES}
- ${PYTHON_LIBRARIES}
- )
在这个示例中实现了两种方法来打印 "Hello, World"。方法一主要使用 Python C API 的方法,二则使用 Boost.Python 提供的 exec 功能。两者实现上没什么区别,后者是也是用方法一实现的,是对 Python C API 的封装,但是提供了更完善的检查和引用计数的处理。所以,如果使用了 Boost.Python,建议使用方法二。一方面更简洁,另一方面也更安全。
我们知道 Python 中的对象是有引用计数的,虽然使用 Python C API 的 Python 对象也有引用计数,但两者还是有区别的,Python 的引用计数由 Python 自动管理,而 Python C API 的引用计数则要求用户手动管理。这就使得对引用计数的处理变得很麻烦,尤其当程序运行异常时尤其难以解决。针对这一问题,Boost.Python 提供了 handle 和 object 类模板来自动化管理引用计数。也就是上面的例子中的 handle<>, object 相关语句。
Boost.Python 提供了四个相关的函数来在 C++ 中调用 Python 代码:
- // eval计算给定的表达式并返回结果值
- object eval(str expression, object globals = object(), object locals = object())
-
- // exec执行 code 指定的 Python 代码段(通常是一组语句)
- object exec(str code, object globals = object(), object locals = object())
-
- // exec_statement 执行 string 指定的 Python 语句
- object exec_statement(str string, object global = object(), object local = object());
-
- // exec_file执行给定文件中包含的代码
- object exec_file(str filename, object globals = object(), object locals = object())
注:上例中的 str 参数也可以使用 const char *代替。
eval(), exec() 和 exec_statement() 三个接口的参数完全一致,应用场景也很接近。区别在于其内部实现里调用的 PyRun_String() 方法的 start 参数分别是 Py_eval_input, Py_file_input 和 Py_single_input。详见:The Very High Level Layer。
由于 exec_statement() 只能执行一行 Python 语句,而 exec() 可以执行一到多行,所以我建议尽量使用后者。
globals 和 locals 分别对应于 PyRun_String() 的同名参数, Boost.Python 在这里做成有默认值的可缺省的参数。
在 Python 中,全局和局部参数是以字典的形式保存的,即{arg_name:arg_value},在大多数情况下,我们可以使用 __main__ 模块来获取这些参数。比如:
- 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"的文件,其中包含 "Hello world!" 内容。
通过 Boost.Python 导入 Python 模块有两种方式,先看一种简单的,直接将 Python 模块的操作直接作为 Python 语句的一部分,然后调用 exec() 来运行:
- object ignored = exec("import sys\n"
- "version = sys.version\n"
- "print(version)\n", main_namespace);
-
- ignored = exec("import sys\n"
- "encode = sys.getdefaultencoding()\n"
- "print(encode)\n", main_namespace);
这种方式简单,但是缺点也显而易见,对模块的操作全都集中在一段代码内还好,但如果操作需要分段进行,则每次都要重新导入模块,效率太低。
一种更灵活的方式是,导入一次模块后,作为一个 C++ 对象保留下来,在任意需要的地方随时调用。Boost.Python 提供的 import() 属性可以很好的用于这种场景,导入后的模块的方法和变量等都作为 C++ 对象的属性。该 import 语句用于导入 Python 模块的调用格式为:
object import(str name)
name 表示要导入的 python 模块名称。
那么上面的示例就可以改写为以下内容:
- object sys = import("sys");
- object version_o = sys.attr("version");
- std::string version_s = extract<std::string>(version_o);
- std::cout << version_s << std::endl;
-
- std::string encode = extract<std::string>(sys.attr("getdefaultencoding")());
- std::cout << encode << std::endl;
注: import() 函数是 Boost.Python 对 PyImport_ImportModule() 函数的封装,其作用与 object main_module((
handle<>(borrowed(PyImport_AddModule("__main__"))))); 语句类似。建议直接使用 import()。
在 C++ 中执行 Python 程序文件,直接使用 exec_file() 即可,本身没什么可说的。但是需要注意两点。
先看示例:
- # embedding.py
- number_x = 30
-
- print ("This is embedded python file")
-
- if __name__ == '__main__':
- number_y = 20
- print ("Py run itself.")
- // embedding.cpp
- int main( int argc, char ** argv )
- {
- try {
- Py_Initialize();
-
- dict global;
- object result = exec_file("embedding.py", global, global);
-
- int num_x = extract<int>(global["number_x"]);
- // int num_y = extract<int>(global["number_y"]); // KeyError: 'number_y'
-
- std::cout << num_x << std::endl;
-
- Py_Finalize();
-
- } catch( error_already_set ) {
- PyErr_Print();
- }
- }
编译后运行结果:
- This is embedded python file
- 30
需要注意的两点是:
我在之前的文章提到过,Boost.Python 使用类来抽象 Python 对象,恰当地使用 object 类及其派生类可以完美地操作所有的 Python 对象。
以下示例用以说明这一事实:
- 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);
接下来我们回到上一个程序,对 C++ 代码稍作修改:
- object main_module = import("__main__");
- object main_namespace = main_module.attr("__dict__");
-
- // dict global;
- object result = exec_file("embedding.py", main_namespace, main_namespace);
-
- int num_x = extract<int>(main_namespace["number_x"]);
- int num_y = extract<int>(main_namespace["number_y"]); // KeyError: 'number_y'
-
- std::cout << num_x << num_y << std::endl;
再次编译运行,结果如下:
- This is embedded python file
- Py run itself.
- 3020
和之前的程序相比,我们这一次在代码里导入了 __main__ 模块,__main__ 是 Python 程序的入口,我们在这里导入了 "__main__" 之后,Python 魔法函数 __name__ 就被赋值了 __main__,那么 .py 文件里的语句也就会被执行了。
在上面这几种方式之外,C++ 程序还可以运行由 Python 作为参数传递进来的方法,此时 C++ 和 Python 之间的交互较为频繁,先由 Python 调用 C++ 接口,再在 C++ 内运行 Python 方法。
具体内容参见:boost.python:在c++程序中调用python的函数
OK,上面的文章的示例是将 python 方法作为一个 boost::python::object 实例传递到 C++ 函数内。除此之外,Boost.Python 还支持另外一种在 C++ 中回调 Python 方法的方法,这个需要用到函数模板 call。
call 函数的使用格式如下:
call<ResultType>(callable_object, a1, a2... aN);
ResultType 是 Python 方法的返回类型会转换成 C++ 的类型, callable_object 是 python 方法名称, a1 - aN 表示 python 方法的参数。
看一个示例,首先在 python 源文件中定义一个方法:
- def add_python(a, b):
- print("Add in python:", a+b)
- return a+b
内容很简单,不需要多说。然后在 cpp 文件中写一个函数来调用该方法:
- int add_c (PyObject *func, int a, int b)
- {
- return python::call<int>(func, a, b);
- }
将 C++ 函数暴露给 python:
def ("Add_c", add_c, return_value_policy<return_by_value>());
最后在 python 程序中调用暴露出来的 C++ 函数:
cp.Add_c(add_python, 2, 3)
这样也可以实现在 C++ 程序中调用 python 方法。是不是觉得有点扯,其实大可不必这么麻烦对不对?我也这样认为,但是作为学习,多了解一种方法也无不可,而且结合其他方法一起用,会有意想不到的效果。
如果在执行 Python 代码的过程当中发生异常,Boost.Python 会抛出 error_already_set 类,该类包含用于管理和转换 Python 和 C++ 异常的类型和函数。
- 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();
- }
- }
嵌入 Python 是为了为我所用,而在前面的示例中,看似 C++ 调用了 Python 代码,但两者仍然是比较孤立的,C++ 内定义的一系列函数和变量等并不能为 Python 代码所使用,这样也起不到结合两者有点的作用。此时,命名空间就有文章可做了。
我们在调用 exec() 时传入了 main_space 作为全局变量,它是一个字典类型的变量,对应于 python 环境下的全局变量字典。格式为 ["name":object] ,也就是说几乎所有类型的 Python 对象都可以作为该变量的成员。当我们在 C++ 程序中初始化 Python 解释器后,main_space 下的成员可以作为 Python 目标的属性直接调用。
比如,我们在 C++ 下定义一个简单的类和函数:
- class mainspace {
- public:
- int get_num() { return 10; }
- };
-
- void showtime()
- {
- std::cout << "show time for namespace" << std::endl;
- }
然后在执行 Python 时将上述类和函数作为成员添加进 Python 全局变量:
- object main_module = import("__main__");
- object main_namespace = main_module.attr("__dict__");
-
- main_namespace["mainspace"] = class_<mainspace>("mainspace")
- .def("get_num",&mainspace::get_num);
- main_namespace["show_time"] = showtime;
-
- object ignored = exec("ms = mainspace()\n"
- "print(ms.get_num())\n"
- "show_time()\n", main_namespace);
此时 Python 全局变量下有两个成员: "mainspace" : class mainspace 和 "show_time" : showtime()。 Python 可以直接使用这两个成员,运行结果如下:
- $ ./embedding
- 10
- show time for namespace
OK,前面我们是将类的定义传递到了 Python 空间,在 Python 运行时要先创建一个类实例,然后才能调用类的成员函数。如果在初始化 Python 解释器之前,我们就有一个类的实例,也可以直接将该实例引用到 Python 空间。
比如,先实例化类,再将该实例赋值给 main_space:
- mainspace aaa;
- ...
- main_namespace["mspace"] = ptr(&aaa);
然后在 Python 下就可以直接使用了:
object ignored = exec("print(mspace.get_num())\n", main_namespace);
于是,我们终于可以访问我们的 C++ 应用程序的数据了,但是所有的数据都在全局命名空间中,对于优秀的程序设计而言并不是一个好的做法。为了避免这种情况并提供适当的封装,我们应该将所有类和数据放入一个模块中。第一步是删除所有修改 main_namespace 对象的行,然后添加一个标准的 Boost 模块定义:
- BOOST_PYTHON_MODULE(CppMod) {
- class_<mainspace>("mainspace")
- .def("get_num",&mainspace::get_num);
- }
现在,在 main() 内部和调用 Py_Initialize() 之前,我们要调用 PyImport_AppendInittab( "CppMod", &initCppMod ); initCppMod 是由 BOOST_PYTHON_MODULE 宏创建的函数,用于初始化 Python 模块 CppMod。此时,我们的嵌入式 python 程序就可以调用 import CppMod,然后作为模块成员访问 mainspace。
前面的内容提到,Boost.Python 使用 handle<> 来实现引用计数的自动管理。句柄本质上是一个指向 Python 对象类型的智能指针;它拥有一个 T* 类型的指针,其中 T 是它的模板参数。 T 必须是派生自 PyObject 的类型或初始 sizeof(PyObject) 字节与 PyObject 布局兼容的 POD 类型。在 Python/'C' API 和高级代码之间的边界处使用 handle<>;首选对象作为 Python 对象的通用接口。
handle 类定义在<boost/python/handle.hpp> 文件中,提供类模板句柄,一个用于管理引用计数的 Python 对象的智能指针。
- namespace boost { namespace python
- {
- template <class T>
- class handle
- {
- typedef T* (handle::* bool_type )() const;
-
- public: // types
- typedef T element_type;
-
- public: // member functions
- ~handle();
-
- template <class Y>
- explicit handle(detail::borrowed<null_ok<Y> >* p);
-
- template <class Y>
- explicit handle(null_ok<detail::borrowed<Y> >* p);
-
- template <class Y>
- explicit handle(detail::borrowed<Y>* p);
-
- template <class Y>
- explicit handle(null_ok<Y>* p);
-
- template <class Y>
- explicit handle(Y* p);
-
- handle();
-
-
- template <typename Y>
- handle(handle<Y> const& r);
- handle(handle const& r);
-
- T* operator-> () const;
- T& operator* () const;
-
- handle& operator=(handle const& r);
- template<typename Y>
- handle& operator=(handle<Y> const & r); // never throws
-
- T* get() const; // 返回当前句柄
- void reset(); // 重置handle类对象,此时句柄指向的地址为NULL
- T* release(); // 返回当前句柄,然后将当前句柄清零
-
- operator bool_type() const; // never throws
- private:
- T* m_p;
- };
-
- template <class T> struct null_ok;
- namespace detail { template <class T> struct borrowed; }
- }}
handle 的构造函数和析构函数实现上依赖于 Python/C API 的 Py_XINCREF() 和 Py_XDECREF() 等操作,用于实现引用计数的加1减1操作。
构造函数的使用格式如下:
- // 创建一个引用计数 +1 的 NULL 指针 x
- handle<> x;
- handle<> x = handle<> ();
-
- // 创建一个基于对象 y 的带有引用计数的指针, null_ok 表示 y 可以为 NULL
- handle<> x(y);
- handle<> x(null_ok(y));
- handle<> x = handle<> (y);
- handle<> x = handle<> (null_ok(y));
-
- // 创建一个与原有对象 y 的引用计数相同的的智能指针x,borrowed()表示对象是“借用”的,保持引用计数一致
- handle<> x(borrowed(y));
- handle<> x(null_ok(borrowed(y)));
- handle<> x = handle<> (borrowed(y));
- handle<> x = handle<> (null_ok(borrowed(y)));
null_ok 和 borrowed 的位置可以互换。
对于大部分的软件工程师来说,了解 exec() 等函数接口就足够了,这部分内容没必要了解。但是学习其具体实现有助于理解 Boost.Python 的实现逻辑,也有助于提升自己的编程能力,所以在这里分析一下。
- object BOOST_PYTHON_DECL exec(str string, object global, object local)
- {
- return exec(python::extract<char const *>(string), global, local);
- }
-
- object BOOST_PYTHON_DECL exec(char const *string, object global, object local)
- {
- // Set suitable default values for global and local dicts.
- if (global.is_none())
- {
- if (PyObject *g = PyEval_GetGlobals())
- global = object(detail::borrowed_reference(g));
- else
- global = dict();
- }
- if (local.is_none()) local = global;
- // should be 'char const *' but older python versions don't use 'const' yet.
- char *s = const_cast<char *>(string);
- PyObject* result = PyRun_String(s, Py_file_input, global.ptr(), local.ptr());
- if (!result) throw_error_already_set();
- return object(detail::new_reference(result));
- }
在没有指定全局和局部变量的情况下,Boost.Python 默认会借助 PyEval_GetGlobals() 来获取当前执行帧中全局变量的字典。如果失败则定义一个新的字典变量。但是头文件中将 global 和 local 设置了默认参数 dict(),所以一般情况下不会运行到 PyEval_GetGlobals()。
调用 Python 的过程也是借助了 PyRun_String() 方法,只是在此基础上增加了异常处理。
eval() 和 exec_statement() 的实现与 exec() 一样,只是在调用 PyRun_String() 的参数 Py_file_input 不同。
exec_file() 的实现由稍许不同,在此不做分析。
在需要调用 Python 方法而又没有将 Python 对象传递到 C++ 环境里的情况下,Boost.Python 提供了两个函数模板族,call 和 call_method,分别用于在 PyObject *s 上调用 Python 函数和方法。 分别定义在 <boost/python/call.hpp> 和 <boost/python/call_method.hpp> 头文件中。调用格式如下:
- call<ResultType>(callable_object, a1, a2... aN);
-
- call_method<ResultType>(self_object, "method-name", a1, a2... aN);
两者的区别在于,前者用于调用 Python 的独立函数,后者用于调用 Python 类对象的方法。
call_method 的示例如下:
C++:
- #include <boost/python/module.hpp>
- #include <boost/python/class.hpp>
- #include <boost/python/call_method.hpp>
- #include <boost/python/def.hpp>
- #include <boost/utility.hpp>
- #include <cstring>
-
- class Base_callback
- {
- public:
- Base_callback(PyObject* self) : m_self(self) {}
-
- char const* class_name() const { return python::call_method<char const*>(m_self, "class_name"); }
-
- private:
- PyObject* const m_self;
- };
-
- bool is_base(Base_callback *b)
- {
- std::cout << "Enter into is_base" << b->class_name() << std::endl;
- return !std::strcmp(b->class_name(), "Base");
- }
-
- BOOST_PYTHON_MODULE(callpython)
- {
- using namespace boost::python;
-
- def("is_base", is_base);
- class_<Base_callback, boost::noncopyable>("Base", init<PyObject*>())
- .def("class_name", &Base_callback::class_name)
- ;
- };
Python:
- # C++ 程序编译生成的库名称为 callpython.so
- import callpython as cp
-
- class Derived():
- def __init__(self,):
- None
-
- def class_name(self):
- print("Derived class name:", self.__class__.__name__)
- return self.__class__.__name__
-
- if __name__ == '__main__':
- ListTest()
- add_python(1, 2)
- cp.Add_c(add_python, 2, 3)
-
- der = Derived()
-
- base = cp.Base(der)
-
- cp.is_base(base)
- print(cp.is_base(base))
-
- base.class_name()
这个示例是根据官网的示例改动的,链接为:boost/python/call_method.hpp - 1.79.0
如果直接编译官网示例运行时,会报如下错误:
ReferenceError: Attempt to return dangling pointer to object of type: char
在这种情况下,由 call_method 调用的 Python 方法会构造一个新的 Python 对象(该 Python 对象包含 C++ 对象)。而我尝试返回该 Python 对象拥有的 C++ 对象的引用。因为被调用的方法返回了一个全新的对象,也就是 class_name() 返回的引用。但是当函数返回时,Python 对象将被销毁,同时该对象的 class_name() 产生的对象也会被销毁,于是该方法返回的引用被悬空。此时如果再对该引用执行任何操作,都将导致程序崩溃并且抛出异常。
Python/C API 是 Python 官方提供的使用 C 或 C++ 扩展 Python 的方法。这种方法需要以特定的方式来编写 C 代码以供 Python 去调用它。所有的 Python 对象都被表示为一种叫做 PyObject 的结构体,并且 Python.h 头文件中提供了各种操作它的函数。大部分对 Python 原生对象的基础函数和操作在 Python.h 头文件中都能找到。
详细教程见:Python/C API Reference Manualhttps://docs.python.org/3/c-api/index.html
以下内容来自于 Boost 源代码,从文章完整性的角度供参考,其中有一些暂时还没有看到的内容,后面会补充文章进行说明。
- // Copyright Stefan Seefeld 2005.
- // Distributed under the Boost Software License, Version 1.0. (See
- // accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
-
- #include <boost/python.hpp>
-
- #include <boost/detail/lightweight_test.hpp>
- #include <iostream>
-
- namespace python = boost::python;
-
- // An abstract base class
- class Base : public boost::noncopyable
- {
- public:
- virtual ~Base() {};
- virtual std::string hello() = 0;
- };
-
- // C++ derived class
- class CppDerived : public Base
- {
- public:
- virtual ~CppDerived() {}
- virtual std::string hello() { return "Hello from C++!";}
- };
-
- // Familiar Boost.Python wrapper class for Base
- struct BaseWrap : Base, python::wrapper<Base>
- {
- virtual std::string hello()
- {
- #if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
- // workaround for VC++ 6.x or 7.0, see
- // http://boost.org/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions
- return python::call<std::string>(this->get_override("hello").ptr());
- #else
- return this->get_override("hello")();
- #endif
- }
- };
-
- // Pack the Base class wrapper into a module
- BOOST_PYTHON_MODULE(embedded_hello)
- {
- python::class_<BaseWrap, boost::noncopyable> base("Base");
- }
-
-
- void exec_test()
- {
- std::cout << "registering extension module embedded_hello..." << std::endl;
-
- // Register the module with the interpreter
- if (PyImport_AppendInittab("embedded_hello", initembedded_hello) == -1)
- throw std::runtime_error("Failed to add embedded_hello to the interpreter's "
- "builtin modules");
-
- std::cout << "defining Python class derived from Base..." << std::endl;
-
- // Retrieve the main module
- python::object main = python::import("__main__");
-
- // Retrieve the main module's namespace
- python::object global(main.attr("__dict__"));
-
- // Define the derived class in Python.
- python::object result = python::exec(
- "from embedded_hello import * \n"
- "class PythonDerived(Base): \n"
- " def hello(self): \n"
- " return 'Hello from Python!' \n",
- global, global);
-
- python::object PythonDerived = global["PythonDerived"];
-
- // Creating and using instances of the C++ class is as easy as always.
- CppDerived cpp;
- BOOST_TEST(cpp.hello() == "Hello from C++!");
-
- std::cout << "testing derived class from C++..." << std::endl;
-
- // But now creating and using instances of the Python class is almost
- // as easy!
- python::object py_base = PythonDerived();
- Base& py = python::extract<Base&>(py_base) BOOST_EXTRACT_WORKAROUND;
-
- // Make sure the right 'hello' method is called.
- BOOST_TEST(py.hello() == "Hello from Python!");
-
- std::cout << "success!" << std::endl;
- }
-
- void exec_file_test(std::string const &script)
- {
- std::cout << "running file " << script << "..." << std::endl;
-
- // Run a python script in an empty environment.
- python::dict global;
- python::object result = python::exec_file(script.c_str(), global, global);
-
- // Extract an object the script stored in the global dictionary.
- BOOST_TEST(python::extract<int>(global["number"]) == 42);
-
- std::cout << "success!" << std::endl;
- }
-
- void exec_test_error()
- {
- std::cout << "intentionally causing a python exception..." << std::endl;
-
- // Execute a statement that raises a python exception.
- python::dict global;
- python::object result = python::exec("print unknown \n", global, global);
-
- std::cout << "Oops! This statement should be skipped due to an exception" << std::endl;
- }
-
- int main(int argc, char **argv)
- {
- BOOST_TEST(argc == 2);
- std::string script = argv[1];
- // Initialize the interpreter
- Py_Initialize();
-
- bool error_expected = false;
-
- if (
- python::handle_exception(exec_test)
- || python::handle_exception(boost::bind(exec_file_test, script))
- || (
- (error_expected = true)
- && python::handle_exception(exec_test_error)
- )
-
- )
- {
- if (PyErr_Occurred())
- {
- if (!error_expected)
- BOOST_ERROR("Python Error detected");
- PyErr_Print();
- }
- else
- {
- BOOST_ERROR("A C++ exception was thrown for which "
- "there was no exception translator registered.");
- }
- }
-
- // Boost.Python doesn't support Py_Finalize yet, so don't call it!
- return boost::report_errors();
- }
boost.python/EmbeddingPython - Python Wiki
boost/python/errors.hpp - 1.79.0
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。