我从c++sig邮件列表中得到了这个绝妙的解决方案。
在C++类中实现一个std::map<std::string, boost::python::object>
,然后重载__getattr__()
和__setattr__()
函数以从该std::map中读取和写入。然后像往常一样使用boost::python::ptr()
将其发送到Python,无需在C++端保留对象或将其发送到Python。它完美地工作。
编辑:我还发现我必须以特殊的方式重写__setattr__()
函数,因为它破坏了我用add_property()
添加的内容。当获取它们时,这些东西运行良好,因为Python在调用__getattr__()
之前检查类的属性,但是对于__setattr__()
没有这样的检查。它直接调用它。因此,我不得不进行一些更改,将其转化为完整的解决方案。下面是解决方案的完整实现:
首先创建一个全局变量:
boost::python::object PyMyModule_global;
创建如下类(可以添加任何其他信息):
class MyClass
{
public:
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];
}
void Py_SetAttr(std::string str, boost::python::object val)
{
try
{
boost::python::object obj = PyMyModule_global["MyClass"].attr(str.c_str());
PyMyModule_global["MyClass"].attr("__setattr_old__")(ptr(this), str, val);
}
catch(boost::python::error_already_set &e)
{
PyErr_Clear();
dict[str] = val;
}
}
private:
std::map<std::string, boost::python::object> dict;
};
然后按照以下方式定义Python模块,添加您想要的任何其他def和属性:
BOOST_PYTHON_MODULE(MyModule)
{
boost::python::class_<MyClass>("MyClass", boost::python::no_init)
.def("__getattr__", &MyClass::Py_GetAttr)
.def("__setattr_new__", &MyClass::Py_SetAttr);
}
然后初始化Python:
void PyInit()
{
PyImport_AppendInittab( "MyModule", &initMyModule );
Py_Initialize();
boost::python::object main = boost::python::import("__main__");
boost::python::object global = main.attr("__dict__");
boost::python::object PyMyModule = boost::python::import("MyModule");
global["MyModule"] = PyMyModule;
PyMyModule_global = PyMyModule.attr("__dict__");
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__
中。
def ProcessEvent(event, obj): return globals()[event](obj.PyObj)
在C++中,我向我的C++类添加了一个名为PyObj
的boost::python::object
,它被初始化为boost::python::ptr(this)
,并使用它调用了我的事件,将我想要调用的事件名称作为第一个参数传递,并将我想要传递给它的对象的boost::python::ptr
作为第二个参数传递。这正如我所希望的那样工作-当我在一个事件中添加了一个属性时,当我将其传递给另一个事件时,它仍然存在。也许不是最好的解决方案...但是,它有效。 - Závada LaCroix