在BOOST_Python_MODULE宏之外将C++实例传递到Python中

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

本文关键字:Python 实例 C++ MODULE BOOST      更新时间:2023-10-16

我想将TestObj的一个实例从C++代码传递到python中。这里发布的代码在cout中产生错误:"没有为C++类型找到to_python(按值)转换器: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;
}

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

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

  • main()中初始化解释器之后,通过class_暴露TestObj
  • 导入静态链接的sfgame模块。这需要通过PyImport_AppendInittab()将模块初始化函数显式添加到Python初始化表中。有关详细信息,请参阅此答案

不建议直接调用模块初始化函数。当直接调用时,模块本身不会创建,但类型将在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 examplen"
      "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.
}

来自文档:

"此宏在使用它的作用域中生成两个函数:extern"C"void initname()和void init_module_name(),其主体必须在宏调用之后。"

您需要拨打initsfgame();