简而言之,与嵌入式Python静态链接的Python扩展需要在初始化解释器之前将其模块初始值设定项函数显式地添加到初始化表中。PyImport_AppendInittab("hello", &inithello);
Py_Initialize();
Python使用^{}宏来定义Python模块初始化器。结果函数不是模块导入器。这种区别类似于创建example.py模块并调用import example。
导入模块时,Python将首先检查模块是否是内置模块。如果模块不在那里,那么Python将搜索module search path试图根据模块名查找Python文件或库。如果找到库,那么Python希望库提供一个函数来初始化模块。一旦找到,导入将在modules表中创建一个空模块,然后对其进行初始化。对于静态链接的模块,例如原始代码中的hello,模块搜索路径将没有帮助,因为没有库供其查找。
对于嵌入,module table and initialization function文档声明,对于静态模块,除非在初始化表中有一个条目,否则不会自动调用模块初始值设定项函数。对于Python 2和Python 3,可以通过在^{}之前调用^{}来实现这一点: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),可能需要对python2使用&inithello,对Python 3使用&PyInit_hello。
下面是一个完整的例子demonstrating让一个嵌入式Python导入一个demo用户模块,然后导入一个静态链接的hello模块。它还调用用户模块demo.multiply中的函数,然后调用通过静态链接模块公开的方法。#include // setenv, atoi
#include // cerr, cout, endl
#include
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")
.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(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
用法:$ ./a.out demo multiply 21 2
foo
Will compute 21 times 2
42
在上面的代码中,我选择使用Boosi.Python代替Python /C API,C++注释用等效的Python代码注释。我发现它更简洁,更不容易出错。如果发生Python错误,Boost.Python将抛出一个异常,所有引用计数都将得到适当处理。
另外,在使用Boost.Python时,不要调用^{}。根据Embedding - Getting started部分:Note that at this time you must not call Py_Finalize() to stop the interpreter. This may be fixed in a future version of boost.python.