Có giải pháp tuyệt vời này từ danh sách gửi thư C++ sig.
Triển khai std::map<std::string, boost::python::object>
trong lớp C++, sau đó quá tải __getattr__()
và __setattr__()
để đọc và ghi vào std :: map đó. Sau đó, chỉ cần gửi nó đến trăn với boost::python::ptr()
như thường lệ, không cần phải giữ một đối tượng xung quanh trên mặt C++ hoặc gửi một đến python. Nó hoạt động hoàn hảo.
Chỉnh sửa: Tôi cũng thấy rằng tôi phải ghi đè chức năng __setattr__()
theo cách đặc biệt vì nó đã phá vỡ những thứ tôi đã thêm với add_property()
. Những điều đó làm việc tốt khi nhận được chúng, vì python kiểm tra các thuộc tính của lớp trước khi gọi __getattr__()
, nhưng không có kiểm tra nào như vậy với __setattr__()
. Nó chỉ gọi nó trực tiếp. Vì vậy, tôi đã phải thực hiện một số thay đổi để biến điều này thành một giải pháp đầy đủ. Dưới đây là thực hiện đầy đủ các giải pháp:
Đầu tiên tạo một biến toàn cầu:
boost::python::object PyMyModule_global;
Tạo một lớp như sau (với bất kỳ thông tin khác mà bạn muốn thêm vào nó):
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;
};
Sau đó, xác định mô-đun python như sau, thêm bất kỳ lỗi và thuộc tính nào bạn muốn:
BOOST_PYTHON_MODULE(MyModule)
{
boost::python::class_<MyClass>("MyClass", boost::python::no_init)
.def("__getattr__", &MyClass::Py_GetAttr)
.def("__setattr_new__", &MyClass::Py_SetAttr);
}
Sau đó khởi python:
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__");
}
Một khi bạn đã làm tất cả những điều này, bạn sẽ có thể giữ lại thay đổi đến dụ thực hiện trong python trên vào thư mục C++. Bất kỳ thứ gì được định nghĩa trong C++ dưới dạng thuộc tính sẽ được xử lý đúng cách và bất kỳ thứ gì không được thêm vào dict
thay vì của lớp là __dict__
.
Tìm thấy một giải pháp.Trong python, tôi đã thêm một hàm: def ProcessEvent (event, obj): return globals() [event] (obj.PyObj) Trong C++, tôi đã thêm một 'boost :: python :: object' đặt tên là 'PyObj' vào lớp C++ của tôi, được khởi tạo thành' boost :: python :: ptr (this) ', và gọi các sự kiện của tôi với nó, chuyển tên của sự kiện mà tôi muốn gọi làm tham số đầu tiên và' boost :: python :: ptr' vào đối tượng mà tôi muốn chuyển cho nó như là thứ hai. Điều đó hoạt động như tôi đã hy vọng - khi tôi thêm một thuộc tính vào một sự kiện, nó vẫn còn đó khi tôi truyền nó cho người khác. Có lẽ không phải là giải pháp tốt nhất ... Nhưng, nó hoạt động. –