将 C++ 实例传递到 BOOST_PYTHON_MODULE 宏之外的 Python

Passing a C++ instance into Python outside of BOOST_PYTHON_MODULE macro

我想将 C++ 代码中的 TestObj 实例传递到 python。此处发布的代码在 cout 中产生错误:"No to_python (by-value) converter found for C++ type: class TestObj"。如果我将对象创建和 main_module.attr("obj") = obj; 移动到 BOOST_PYTHON_MODULE 宏中,代码运行正常。

当我尝试传递带有或不带有 boost::ptr 的 *TestObj 时会发生类似的事情。

testembed.py:

import sfgame

print("old x: " + str(obj.x))
obj.x = 10
print("new x: " + str(obj.x))

testobj.h

class TestObj{
public:
    TestObj();
    int x;
    int getX();
    void setX(int xv);
};

testobj.cpp

#include "TestObj.h"
TestObj::TestObj(){
}

int TestObj::getX(){
    return x;
}

void TestObj::setX(int xv){
    x = xv;
}

main.cpp

#include <boost/python.hpp>
#include "TestObj.h"

using namespace boost::python;

BOOST_PYTHON_MODULE(sfgame){
    class_<TestObj>("TestObj")
        .add_property("x", &TestObj::getX, &TestObj::setX)
        ;
}

int main(){
    Py_Initialize();

    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");

    TestObj obj;

    try{
        obj.setX(5);
        main_module.attr("obj") = obj;

        exec_file("testembed.py", main_namespace);
    }
    catch (const boost::python::error_already_set &){
        PyObject *ptype, *pvalue, *ptraceback;
        PyErr_Fetch(&ptype, &pvalue, &ptraceback);
        std::string error;
        error = boost::python::extract<std::string>(pvalue);
        std::cout << error << std::endl;
    }

    system("PAUSE");
    return 0;
}

来自文档:

"This macro generates two functions in the scope where it is used: extern "C" void initname(), and void init_module_name(),其主体必须跟在宏调用之后。"

您需要致电initsfgame();

通过 Boost.Python 将 C++ 对象传递给 Python 具有相同的先决条件,无论调用它的上下文如何:必须为 Python 转换器注册C++ 对象的类型。

创建 boost::python::class_<T>, to-Python and from-Python converters are automatically registered for type T. The BOOST_PYTHON_MODULE 宏的实例时,只声明一个 Python 模块初始化函数,Python 将在导入模块时调用该函数。在这种特殊情况下,可以通过在将 TestObj 实例传递给 Python 之前执行以下任一操作来解决问题:

  • 解释器在 main() 内初始化后,通过 class_ 公开 TestObj
  • 导入静态链接的sfgame模块。这需要通过 PyImport_AppendInittab(). See 答案显式添加模块初始化函数 Python 初始化 table。

不推荐直接调用模块初始化函数。直接调用时,不会创建模块本身,但会使用 Boost.Python 注册类型。导入模块后,将创建并初始化模块,从而再次注册类型。在 Boost.Python 的调试版本中,这将使断言失败,而在发布版本中,它将打印警告。


这是一个完整的示例 demonstrating 在嵌入时将 C++ 对象传递给 Python。在示例中,spam 类型在静态链接的 example 模块中公开,而 egg 类型在 __main__ 范围内公开。

#include <boost/python.hpp>

// Mockup models.
struct spam {};
struct egg {};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<spam>("Spam", python::init<>());
}

int main()
{
  // Explicitly add initializers for staticly linked modules.
  PyImport_AppendInittab("example", &initexample);

  // Initialize Python.
  Py_Initialize();

  namespace python = boost::python;
  try
  {
    // Create the __main__ module.
    python::object main_module = python::import("__main__");
    python::object main_namespace = main_module.attr("__dict__");

    // Import the example module, this will cause the example module's
    // initialization function to be invoked, registering the spam type.
    // >>> import example
    python::import("example");

    // >>> spam = example.Spam()
    spam spam;
    main_namespace["spam"] = spam;

    // Expose egg, defining it within the main module.
    // >>> class Egg: pass
    main_namespace["Egg"] = python::class_<egg>("Egg", python::init<>());
    // >>> egg = Egg()
    egg egg;
    main_namespace["egg"] = egg;

    // Verify variables and their types.
    python::exec(
      "import example\n"
      "assert(isinstance(spam, example.Spam))\n"
      "assert(isinstance(egg, Egg))\n",
      main_namespace);
  }
  catch (const python::error_already_set&)
  {
    PyErr_Print();
    return 1;
  }

  // Do not call Py_Finalize() with Boost.Python.
}