2012-12-05 99 views
5

因此,我正在开发一个小型项目,在该项目中,我将Python用作嵌入式脚本引擎。到目前为止,使用boost.python我没有遇到太多麻烦,但是如果可能的话,我想用它做些什么。Boost.Python - 将boost :: python :: object作为参数传递给python函数?

基本上,Python可以通过向类中添加函数甚至数据值来扩展我的C++类。我希望能够将这些持久化在C++端,所以一个python函数可以将数据成员添加到类中,然后传递给不同函数的同一个实例仍然会拥有它们。这里的目标是用C++编写一个通用的核心引擎,并让用户以任何他们需要的方式将其扩展到Python,而无需触及C++。

所以,我认为会的工作是,我会在C++类boost::python::object存储为值self,并调用从C++蟒蛇的时候,我会通过boost::python::ptr()发送Python对象,以便在修改python方面会坚持回到C++类。不幸的是,当我尝试,我得到了以下错误:

TypeError: No to_python (by-value) converter found for C++ type: boost::python::api::object

是否有直接传递对象到Python函数一样,任何方式或任何其他方式,我可以去这个实现我想要的结果?

在此先感谢您的帮助。 :)

+0

找到了一个解决方案。在python中,我添加了一个函数: def ProcessEvent(event,obj): return globals()[event](obj.PyObj) 在C++中,我添加了一个'boost :: python :: object'给我的C++类命名'PyObj',该类初始化为'boost :: python :: ptr(this)',并用它来调用我的事件,传递我想调用的事件的名称作为第一个参数, boost :: python :: ptr'作为第二个我想传递给它的对象。这符合我的希望 - 当我在一个事件中添加属性时,它在我传递给另一个事件时仍然存在。也许不是最好的解决方案...但是,它的工作原理。 –

回答

5

从C++的邮件列表中得到了这个奇妙的解决方案。

在C++类中实现std::map<std::string, boost::python::object>,然后重载__getattr__()__setattr__()来读取和写入std :: map。然后像往常一样将它发送到Python的boost::python::ptr(),不需要在C++端保留一个对象或发送一个到python。它完美的作品。

编辑:我也发现我不得不以一种特殊的方式覆盖__setattr__()函数,因为它破坏了我添加的add_property()。这些东西在得到它们的时候工作得很好,因为python在调用__getattr__()之前检查了一个类的属性,但是__setattr__()没有这样的检查。它只是直接调用它。所以我必须做出一些改变才能把它变成一个完整的解决方案。下面是完整的实施解决方案:

首先创建一个全局变量:

boost::python::object PyMyModule_global; 

创建一个类,如下所示(以要添加到它的任何其他信息):

class MyClass 
{ 
public: 
    //Python checks the class attributes before it calls __getattr__ so we don't have to do anything special here. 
    boost::python::object Py_GetAttr(std::string str) 
    { 
     if(dict.find(str) == dict.end()) 
     { 
     PyErr_SetString(PyExc_AttributeError, JFormat::format("MyClass instance has no attribute '{0}'", str).c_str()); 
     throw boost::python::error_already_set(); 
     } 
     return dict[str]; 
    } 

    //However, with __setattr__, python doesn't do anything with the class attributes first, it just calls __setattr__. 
    //Which means anything that's been defined as a class attribute won't be modified here - including things set with 
    //add_property(), def_readwrite(), etc. 
    void Py_SetAttr(std::string str, boost::python::object val) 
    { 
     try 
     { 
     //First we check to see if the class has an attribute by this name. 
     boost::python::object obj = PyMyModule_global["MyClass"].attr(str.c_str()); 
     //If so, we call the old cached __setattr__ function. 
     PyMyModule_global["MyClass"].attr("__setattr_old__")(ptr(this), str, val); 
     } 
     catch(boost::python::error_already_set &e) 
     { 
     //If it threw an exception, that means that there is no such attribute. 
     //Put it on the persistent dict. 
     PyErr_Clear(); 
     dict[str] = val; 
     } 
    } 
private: 
    std::map<std::string, boost::python::object> dict; 
}; 

然后定义python模块,如下所示,添加任何其他的defs和你想要的属性:

BOOST_PYTHON_MODULE(MyModule) 
{ 
    boost::python::class_<MyClass>("MyClass", boost::python::no_init) 
     .def("__getattr__", &MyClass::Py_GetAttr) 
     .def("__setattr_new__", &MyClass::Py_SetAttr); 
} 

然后初始化蟒蛇:

void PyInit() 
{ 
    //Initialize module 
    PyImport_AppendInittab("MyModule", &initMyModule); 
    //Initialize Python 
    Py_Initialize(); 

    //Grab __main__ and its globals 
    boost::python::object main = boost::python::import("__main__"); 
    boost::python::object global = main.attr("__dict__"); 

    //Import the module and grab its globals 
    boost::python::object PyMyModule = boost::python::import("MyModule"); 
    global["MyModule"] = PyMyModule; 
    PyMyModule_global = PyMyModule.attr("__dict__"); 

    //Overload MyClass's setattr, so that it will work with already defined attributes while persisting new ones 
    PyMyModule_global["MyClass"].attr("__setattr_old__") = PyMyModule_global["MyClass"].attr("__setattr__"); 
    PyMyModule_global["MyClass"].attr("__setattr__") = PyMyModule_global["MyClass"].attr("__setattr_new__"); 
} 

一旦你完成了这一切,你就可以坚持的变化在Python转移到C++进行的实例。任何在C++中定义为属性的东西都会被正确处理,任何不会被附加到dict而不是类__dict__

+0

太棒了!感谢分享! – mrmclovin

相关问题