该代码使用 Python 2 标头配置进行编译。当使用 Python 3 标头配置进行编译时,boost/python/module_init.hpp
会将embedded_hello
模块的初始化函数声明为PyInit_embedded_hello
而不是initembedded_hello
. 我强烈建议验证正确的头文件配置,并执行干净的 Boost.Python 构建,因为 Boost.Python 和使用该库构建的模块需要使用相同的头文件配置。
此外,在将模块添加到内置表时,PyImport_AppendInittab()
调用需要发生在Py_Initialize()
. 该PyImport_AppendInittab()
文档明确指出:
将单个模块添加到现有的内置模块表中。...这应该在之前调用Py_Initialize()
。
Boost.Python 使用BOOST_PYTHON_MODULE
宏来定义 Python 模块。在模块的主体内,当前范围是模块本身。因此,当 C++ 类型通过类型包装器公开时,例如当 C++ 类通过 公开给 Python 时boost::python::class_
,生成的 Python 类将位于由BOOST_PYTHON_MODULE
.
另一方面,在 Python 中声明的用户定义类型是一等对象。从 C++ 的角度来看,它们可以被视为工厂函数。因此,要在 C++ 中使用 Python 定义的类,需要获取类对象的句柄,然后通过调用类对象来实例化类的实例。
这是一个完整的最小示例,演示了嵌入 Python 3 解释器:
- 导入一个
example
已直接构建到二进制文件中的模块 (),spam_wrap
并向 Python ( example.Spam
) 公开一个基本的 C++ 类 (),该类具有默认的虚函数/调度。
- 演示使用公开的 Python 类 (
example.Spam
)。
- 从 Python ( ) 中公开的 Python 类 (
example.Spam
)派生example.PySpam
并使用生成的类。
#include <iostream>
#include <boost/python.hpp>
/// @brief Mockup Spam model.
struct spam
: boost::noncopyable
{
virtual ~spam() {};
virtual std::string hello() { return "Hello from C++"; }
};
//@ brief Mockup Spam wrapper.
struct spam_wrap
: spam,
boost::python::wrapper<spam>
{
virtual std::string hello()
{
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)
return boost::python::call<std::string>(
this->get_override("hello").ptr());
#else
return this->get_override("hello")();
#endif
}
std::string default_hello() { return this->spam::hello(); }
};
/// @brief Python example module.
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose C++ spam_wrap as Python Spam class.
python::class_<spam_wrap, boost::noncopyable>("Spam")
.def("hello", &spam::hello, &spam_wrap::default_hello)
;
}
int main()
{
// Add example to built-in.
PyImport_AppendInittab("example", &PyInit_example);
// Start the interpreter.
Py_Initialize();
namespace python = boost::python;
try
{
python::object main = python::import("__main__");
python::object global = main.attr("__dict__");
// Execute Python code, using the example module.
exec(
"from example import Spam \n"
"spam = Spam() \n"
" \n"
"class PySpam(Spam): \n"
" def hello(self): \n"
" return 'Hello from Python'\n",
global, global);
/// Check the instance of the Python object using the C++ class.
// >>> spam_object = spam
python::object spam_object = global["spam"];
assert(python::extract<spam>(spam_object).check());
// >>> result = spam_object.hello()
python::object result = spam_object.attr("hello")();
// >>> print(result)
std::cout << python::extract<std::string>(result)() << std::endl;
// >>> assert("Hello from C++" == result)
assert("Hello from C++" == python::extract<std::string>(result)());
/// Create an instance using PySpam class. It too is a Python object.
// >>> py_spam_type = PySpam
python::object py_spam_type = global["PySpam"];
// >>> py_spam_object = py_spam_type()
python::object py_spam_object = py_spam_type();
// >>> result = py_spam_object()
result = py_spam_object.attr("hello")();
// >>> print(result)
std::cout << python::extract<std::string>(result)() << std::endl;
// >>> assert("Hello from Python" == result)
assert("Hello from Python" == python::extract<std::string>(result)());
}
catch (const python::error_already_set&)
{
PyErr_Print();
}
}
该程序应该运行完成而没有错误,产生以下输出:
Hello from C++
Hello from Python