【问题标题】:Passing a C++ instance into Python outside of BOOST_PYTHON_MODULE macro在 BOOST_PYTHON_MODULE 宏之外将 C++ 实例传递给 Python
【发布时间】:2015-11-28 15:58:34
【问题描述】:

我想将 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;
}

【问题讨论】:

    标签: python c++ boost boost-python


    【解决方案1】:

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

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

    • main() 中初始化解释器后,通过class_ 公开TestObj
    • 导入静态链接的sfgame 模块。这需要通过PyImport_AppendInittab() 将模块初始化函数显式添加到 Python 初始化表中。详情见this回复。

    不推荐直接调用模块初始化函数。直接调用时,不会创建模块本身,但类型会注册到 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.
    }
    

    【讨论】:

    • 很棒的答案,我到处找这种例子。
    【解决方案2】:

    来自文档:

    “这个宏在使用它的范围内生成两个函数:extern "C" void initname() 和void init_module_name(),其主体必须跟在宏调用之后。”

    您需要致电initsfgame();

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-21
      • 1970-01-01
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多