简而言之,与嵌入式 Python 静态链接的 Python 扩展需要在解释器初始化之前将其模块初始化函数显式添加到初始化表中。
PyImport_AppendInittab("hello", &inithello);
Py_Initialize();
Boost.Python 使用BOOST_PYTHON_MODULE用于定义 Python 模块初始值设定项的宏。生成的函数不是模块导入器。这种差异类似于创建example.py
模块及调用import example
.
当导入一个模块时,Python会首先检查该模块是否是内置模块。如果该模块不存在,那么 Python 将搜索模块搜索路径尝试根据模块名称查找 python 文件或库。如果找到一个库,那么 Python 期望该库提供一个将初始化模块的函数。一旦找到,导入将在模块表中创建一个空模块,然后对其进行初始化。对于静态链接的模块,例如hello
在原始代码中,模块搜索路径没有帮助,因为没有可供它查找的库。
对于嵌入,模块表和初始化函数文档指出,对于静态模块,除非初始化表中有条目,否则不会自动调用模块初始化函数。对于 Python 2 和 Python 3,可以通过调用来完成此操作PyImport_AppendInittab() before Py_Initialize():
BOOST_PYTHON_MODULE(hello)
{
// ...
}
PyImport_AppendInittab("hello", &inithello);
Py_Initialize();
// ...
boost::python::object hello = boost::python::import("hello");
另请注意,用于嵌入的 Python C API 更改了 Python 2 和 3 之间模块初始化函数的命名约定,因此对于BOOST_PYTHON_MODULE(hello)
,可能需要使用&inithello
对于 Python 2 和&PyInit_hello
对于Python 3。
这是一个完整的例子展示有一个嵌入式Python导入demo
用户模块,然后将导入静态链接的hello
模块。它还调用用户模块中的函数demo.multiply
,然后将调用通过静态链接模块公开的方法。
#include <cstdlib> // setenv, atoi
#include <iostream> // cerr, cout, endl
#include <boost/python.hpp>
struct World
{
void set(std::string msg) { this->msg = msg; }
std::string greet() { return msg; }
std::string msg;
};
/// Staticly linking a Python extension for embedded Python.
BOOST_PYTHON_MODULE(hello)
{
namespace python = boost::python;
python::class_<World>("World")
.def("greet", &World::greet)
.def("set", &World::set)
;
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
std::cerr << "Usage: call pythonfile funcname [args]" << std::endl;
return 1;
}
char* module_name = argv[1];
char* function_name = argv[2];
// Explicitly add initializers for staticly linked modules.
PyImport_AppendInittab("hello", &inithello);
// Initialize Python.
setenv("PYTHONPATH", ".", 1);
Py_Initialize();
namespace python = boost::python;
try
{
// Convert remaining args into a Python list of integers.
python::list args;
for (int i=3; i < argc; ++i)
{
args.append(std::atoi(argv[i]));
}
// Import the user requested module.
// >>> import module
python::object module = python::import(module_name);
// Invoke the user requested function with the provided arguments.
// >>> result = module.fn(*args)
python::object result = module.attr(function_name)(*python::tuple(args));
// Print the result.
std::cout << python::extract<int>(result)() << std::endl;
}
catch (const python::error_already_set&)
{
PyErr_Print();
return 1;
}
// Do not call Py_Finalize() with Boost.Python.
}
内容demo.py
:
import hello
planet = hello.World()
planet.set('foo')
def multiply(a,b):
print planet.greet()
print "Will compute", a, "times", b
c = 0
for i in range(0, a):
c = c + b
return c
Usage:
$ ./a.out demo multiply 21 2
foo
Will compute 21 times 2
42
在上面的代码中,我选择使用 Boost.Python 而不是 Python/C API,并用等效的 Python 代码注释了 C++ 注释。我发现它更加简洁并且不易出错。如果发生 Python 错误,Boost.Python 将抛出异常,并且所有引用计数都将得到适当处理。
另外,当使用 Boost.Python 时,不要调用Py_Finalize()。根据嵌入 - 入门部分:
注意此时一定不能调用Py_Finalize()
停止翻译。这可能会在 boost.python 的未来版本中得到修复。