Передача экземпляров классов С++ в python с boost:: python
У меня есть библиотека, которая создает объекты (экземпляры класса A) и передает их в программу python, которая должна иметь возможность вызывать их методы.
В основном у меня есть экземпляры класса С++, и я хочу использовать их из python. Иногда этот объект должен быть возвращен на С++ для некоторых манипуляций.
Я создал следующий файл-оболочку (предположим, что функция New
вызывается где-то в коде С++):
#include <boost/python.hpp>
#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace boost;
using namespace boost::python;
int calls = 0;
struct A
{
int f() { return calls++; }
~A() { std::cout << "destroyed\n"; }
};
shared_ptr<A> existing_instance;
void New() { existing_instance = shared_ptr<A>( new A() ); }
int Count( shared_ptr<A> a ) { return a.use_count(); }
BOOST_PYTHON_MODULE(libp)
{
class_<A>("A")
.def("f", &A::f)
;
def("Count", &Count);
register_ptr_to_python< shared_ptr<A> >();
}
В коде отсутствует часть, в которой python получает existing_instance
. Я не вставлял это, но позвольте сказать, что для этой цели я использую механизм обратного вызова.
Этот код работает, но у меня есть несколько вопросов:
-
В функции Count (и во всех других функциях манипуляции с С++) это нормально передать a
, как это, или лучше сделать что-то вроде const shared_ptr<A>&
? В фрагментах кода, которые я нашел в документации по форсированию python, часто используется ссылка, но я не понимаю разницы (кроме, конечно, более высокого ссылочного счетчика).
-
Является ли этот код "безопасным"? Когда я передаю существующий_инстамент на python, его счетчик будет увеличиваться (только один раз, даже если в python я делаю больше копий объекта, конечно), поэтому нет никакого способа, чтобы код С++ мог уничтожить объект, поскольку python имеет место по крайней мере, "копией". Я прав? Я пытался играть с указателями, и, похоже, я прав, я просто хочу быть уверен.
-
Я хочу, чтобы python не создавал экземпляры A. Они должны передаваться только из кода С++. Как я мог это достичь? EDIT: найдено, мне просто нужно использовать no_init и noncopyable: class_<A, boost::noncopyable>("A", no_init)
Ответы
Ответ 1
boost::python
знает все о boost::shared_ptr
, но вам нужно сказать, что boost::shared_ptr<A>
содержит экземпляр A
, вы делаете это, добавляя boost::shared_ptr<A>
в список аргументов шаблона к class_
, больше информация об этом "Held Type" находится в здесь, в документации по ускорению.
Чтобы предотвратить создание экземпляров из python, вы добавляете boost::python::no_init
к конструктору класса, поэтому вы получаете:
boost::python::class_< A, boost::shared_ptr<A> >("A", boost::python::no_init)
//... .def, etc
;
В общем случае вы не должны передавать общие указатели по ссылке, так как если ссылка на общий указатель недействительна, то ссылка, на которую указывает указатель общего указателя, также недействительна (поскольку выбор ссылки на общий указатель 't увеличивать контрольный счетчик до указанного объекта).
Абсолютно безопасно передавать объекты boost::shared_ptr
вокруг и из python, подсчет ссылок (python и shared_ptr) будет корректно управляться, если вы не измените return_value_policy
. Если вы измените политику метода, выставленного на python, чтобы он возвращал ссылку на общий указатель, тогда вы можете создавать проблемы, так же, как передача общих указателей вокруг ссылок С++ может вызвать проблемы.
(Кроме того, вы должны использовать make_shared<A>(...)
вместо shared_ptr<A>(new A(...))
.)
Ответ 2
В этой ситуации мой код будет выглядеть так (для вашего примера):
...
BOOST_PYTHON_MODULE(libp)
{
class_<A, boost::shared_ptr<A>, boost::noncopyable >("A")
.def("f", &A::f)
.def("Count", &Count)
;
}
Важно запретить boost:: python копировать вещи, но если вы используете
Вероятность shared_ptr заключается в том, что вам нужно копировать только в нескольких контролируемых ситуациях.