Boost.Python — Передача boost :: python :: object в качестве аргумента функции python?

Поэтому я работаю над небольшим проектом, в котором я использую Python в качестве встроенного скриптового движка. До сих пор у меня не было особых проблем с использованием boost.python, но я бы хотел кое-что с ним сделать, если это возможно.

По сути, Python можно использовать для расширения моих классов C ++, добавляя в класс функции и даже значения данных. Мне бы хотелось, чтобы они сохранялись на стороне C ++, поэтому одна функция python может добавлять члены данных в класс, а затем тот же экземпляр, переданный другой функции, все равно будет иметь их. Цель здесь состоит в том, чтобы написать общий базовый движок на C ++ и позволить пользователям расширять его на Python любым способом, в котором они нуждаются, даже не касаясь C ++.

Так что я думал, что будет работать, что я буду хранить boost::python::object в классе C ++ в качестве значения selfи при вызове python из C ++ я бы отправил этот объект через boost::python::ptr(), так что изменения на стороне Python будут сохраняться обратно в класс C ++. К сожалению, когда я пытаюсь это сделать, я получаю следующую ошибку:

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

Есть ли способ передать объект непосредственно в функцию Python, подобную этой, или каким-либо другим способом, которым я могу сделать это, чтобы достичь желаемого результата?

Заранее благодарю за любую помощь. 🙂

5

Решение

Получил это фантастическое решение из списка рассылки c ++ sig.

Реализовать std::map<std::string, boost::python::object> в классе C ++, затем перегруз __getattr__() а также __setattr__() читать и писать на эту std :: map. Тогда просто отправьте его на питона с 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 следующим образом, добавив любые другие определения и свойства, которые вы хотите:

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()
{
//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__,

5

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector