Dostałam to fantastyczne rozwiązanie z listy wysyłkowej C++ sig.
Zaimplementuj std::map<std::string, boost::python::object>
w klasie C++, następnie przeładuj __getattr__()
i __setattr__()
, aby odczytać i zapisać na tym std :: map. Następnie po prostu wyślij go do Pythona z boost::python::ptr()
jak zwykle, nie musisz przechowywać obiektu po stronie C++ lub wysłać go do Pythona. Działa idealnie.
Edycja: Zauważyłem również, że musiałem nadpisać funkcję __setattr__()
w specjalny sposób, ponieważ było to zrywanie rzeczy dodanych z add_property()
. Te rzeczy działały poprawnie podczas ich pobierania, ponieważ pyton sprawdza atrybuty klasy przed wywołaniem __getattr__()
, ale nie ma takiej kontroli z __setattr__()
. Po prostu nazywa to bezpośrednio. Musiałem więc wprowadzić pewne zmiany, aby zmienić to w pełne rozwiązanie. Oto pełne wdrożenie rozwiązania:
najpierw utworzyć zmienną globalną:
boost::python::object PyMyModule_global;
utworzyć klasę następująco (z tym, co inne informacje, które chcesz dodać do niego):
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;
};
Następnie zdefiniuj moduł python w następujący sposób, dodając wszelkie inne defy i właściwości, które chcesz:
Następnie zainicjowania Pythona:
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__");
}
Po wykonaniu tego wszystkiego, będziesz w stanie utrzymywać się zmiany dokonane w instancji pytona nad do C++. Cokolwiek zdefiniowane w C++ jako atrybut zostanie poprawnie obsłużone, a wszystko, czego nie ma, zostanie dodane do dict
zamiast do klasy __dict__
.
Znaleziono jedno rozwiązanie.W pythonie dodałem funkcję: def ProcessEvent (event, obj): return globals() [event] (obj.PyObj) W C++ dodałem 'boost :: python :: object' o nazwie 'PyObj' do mojej klasy C++, która jest inicjowana do' boost :: python :: ptr (this) 'i wywołała moje zdarzenia z tym, przekazując nazwę zdarzenia, które chcę nazwać jako pierwszy parametr i' boost :: python :: ptr' do obiektu, który chcę przekazać jako drugi. Działa to tak, jak się spodziewałem - kiedy dodałem atrybut w jednym zdarzeniu, wciąż tam był, kiedy przekazywałem go innemu. Może nie najlepsze rozwiązanie ... Ale działa. –