在BOOST_PYTHON_MODULE宏之外将C++实例传递给Python

Sam*_*TER 1 c++ python boost boost-python

我想将一个TestObj实例从C++代码传递给python.这里发布的代码在cout中产生错误:"为C++类型找不到to_python(by-value)转换器:class TestObj".如果我将对象创建移动main_module.attr("obj") = obj;到BOOST_PYTHON_MODULE宏中,代码运行正常.

当我尝试*TestObj使用或不使用boost :: ptr 时,会发生类似的事情.

testembed.py:

import sfgame

print("old x: " + str(obj.x))
obj.x = 10
print("new x: " + str(obj.x))
Run Code Online (Sandbox Code Playgroud)

testobj.h

class TestObj{
public:
    TestObj();
    int x;
    int getX();
    void setX(int xv);
};
Run Code Online (Sandbox Code Playgroud)

testobj.cpp

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

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

void TestObj::setX(int xv){
    x = xv;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

Tan*_*ury 5

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

在创建实例时boost::python::class_<T>,to-Python和from-Python转换器会自动注册类型T.该BOOST_PYTHON_MODULE宏仅声明Python模块初始化函数,Python将在导入模块时调用该函数.在这种特殊情况下,可以通过在将TestObj实例传递给Python 之前执行以下任一操作来解决问题:

  • 在解释器初始化之后公开TestObj通过.class_main()
  • 导入静态链接的sfgame模块.这需要通过Python初始化表明确添加模块初始化函数PyImport_AppendInittab().有关详情,请参阅答案.

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


这是一个完整的示例,演示了嵌入时将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.
}
Run Code Online (Sandbox Code Playgroud)