为编写 C++ 游戏脚本设置嵌入式 Python

Setting up embedded Python for Scripting a C++ Game

我很难做到这一点。我坚持的是试图将用 C++ 编写的模块公开给嵌入式 python 解释器。

我正在使用 boost::python,但我不确定我应该为此做什么,至少可以说,似乎缺少文档。

我想要的是用 BOOST_PYTHON_MODULE 公开一些 C++ 代码,然后从 同一个应用程序 访问它。但是我无法导入它。我得到的,这似乎是最接近的(只是相关部分):

#include <python/interpreter.hpp>

bp::object blag() {
    return bp::str("Thingy");
}

BOOST_PYTHON_MODULE(modthingy) {
    bp::def("blag", &blag);
}


Interpreter::Interpreter() {
    Py_UnbufferedStdioFlag = 1;
    Py_Initialize();
    try {
        init_module_modthingy();
    } catch (bp::error_already_set) {
        PyErr_Print();
    }

    main_module = bp::import("__main__");
    main_namespace = main_module.attr("__dict__");
}

但是打印错误 AttributeError: 'NoneType' object has no attribute '__dict__' 而且我以后无法导入模块。

这应该如何构建?

编辑: 好的,所以我得到的最接近的是接受的答案中的一种方法:

PyImport_AppendInittab("modthingy", &PyInit_modthingy);
Py_Initialize();

但是,这对我来说似乎不是特别有用,因为我希望能够在 Initialize 函数之后 add/import 模块。我将研究一些事情,即:

我会根据我的发现更新此 post。

Boost.Python 使用 BOOST_PYTHON_MODULE 宏来定义 Python 模块初始值设定项。结果函数不是模块导入器。这种差异类似于创建 modthingy.py 模块并调用 import modthingy.

导入模块时,Python会先检查模块是否为内置模块。如果模块不存在,那么 Python 将搜索 module search path 并尝试根据模块名称查找 python 文件或库。如果找到一个库,那么 Python 期望该库提供一个函数来初始化模块。一旦找到,导入将在模块 table 中创建一个空模块,然后对其进行初始化。对于静态链接的模块,例如 modthingy,模块搜索路径将无济于事,因为没有可供它查找的库。

对于嵌入,module table and initialization function documentation states that for static modules, the module initializer function will not be automatically called unless there is an entry in the initialization table. For Python 2 and Python 3, one can accomplish this by calling PyImport_AppendInittab() before Py_Initialize():

BOOST_PYTHON_MODULE(modthingy)
{
  // ...
}

PyImport_AppendInittab("modthingy", &initmodthingy);
Py_Initialize();
// ...
boost::python::object modthingy = boost::python::import("modthingy");

或者,对于 Python 2,一旦解释器被初始化,就可以创建一个空模块,通过 PyImport_AddModule() 添加到模块字典,然后显式初始化模块。

BOOST_PYTHON_MODULE(modthingy)
{
  // ...
}

Py_Initialize();
PyImport_AddModule("modythingy");
initmodthingy();
boost::python::object modthingy = boost::python::import("modthingy");

此方法在官方 Python 嵌入式演示 embed/demo.c 中进行了演示。从 BOOST_PYTHON_MODULE 创建的模块初始值设定项不调用 PyImport_AddModule(),因此必须显式调用它。

另请注意,用于嵌入的 Python 的 C API 在 Python 2 和 3 之间更改了模块初始化函数的命名约定,因此对于 BOOST_PYTHON_MODULE(modthingy),一个可能需要对 Python 2 使用 &initmodthingy,对 Python 3 使用 &PyInit_modthingy


这是一个最小的完整示例,演示了导入与嵌入式解释器静态链接的模块:

#include <iostream>
#include <string>

#include <boost/python.hpp>

std::string spam() { return "Spam spam spam"; }

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::def("spam", &spam);
}

int main()
{
  // Add example to built-in.
  PyImport_AppendInittab("example", &initexample);

  // Start the interpreter.
  Py_Initialize();

  namespace python = boost::python;
  try
  {
    // >>> import example
    python::object example = python::import("example");
    // >>> x = example.spam()
    python::object x = example.attr("spam")();
    // >>> print x
    std::cout << "x = " << python::extract<std::string>(x)() << std::endl;
  }
  catch (const python::error_already_set&)
  {
    PyErr_Print();
  }
}

输出:

x = Spam spam spam