передача экземпляров классов C ++ в python с помощью boost :: python - PullRequest
13 голосов
/ 27 июля 2010

У меня есть библиотека, которая создает объекты (экземпляры класса A) и передает их программе на python, которая должна иметь возможность вызывать их методы.

В основном у меня есть экземпляры класса C ++, и я хочу использовать их из python. Иногда этот объект должен быть передан обратно в C ++ для некоторых манипуляций.

Я создал следующий файл оболочки (предположим, что функция New вызывается где-то в коде C ++):

#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> >();
} 

В коде отсутствует часть, где питон получает existing_instance. Я не вставил это, но давайте просто скажем, что я использую механизм обратного вызова для этой цели.

Этот код работает, но у меня есть несколько вопросов:

  1. В функции Count (и во всех других функциях манипуляции C ++) нормально передавать a таким образом или лучше сделать что-то вроде const shared_ptr<A>&? В фрагментах кода, которые я нашел в документации по поддержке Python, ссылка часто используется, но я не понимаю разницы (конечно, кроме более высокого счетчика ссылок).

  2. Является ли этот код "безопасным"? Когда я передаю существующий_экземпляр в python, его счетчик будет увеличиваться (только один раз, даже если в python я, конечно, создаю больше копий объекта), поэтому нет никакого способа, которым код C ++ мог бы уничтожить объект настолько, насколько в нем хранится python. по крайней мере, «копия». Я прав? Я пытался играть с указателями, и, похоже, я прав, просто прошу быть уверенным.

  3. Я бы хотел запретить Python создавать экземпляры A. Они должны передаваться только из кода C ++. Как я мог этого добиться? РЕДАКТИРОВАТЬ : найдено, мне просто нужно использовать no_init и noncopyable: class_<A, boost::noncopyable>("A", no_init)

Ответы [ 2 ]

14 голосов
/ 31 июля 2010

boost::python знает все о boost::shared_ptr, но вы должны сказать ему, что boost::shared_ptr<A> содержит экземпляр A, вы делаете это, добавляя boost::shared_ptr<A> в списке аргументов шаблона к class_, большеинформация об этом 'Held Type' здесь, в документации по бустеру .

Чтобы предотвратить создание экземпляров из python, вы добавляете boost::python::no_init в конструктор class_, так что вы получите

boost::python::class_< A, boost::shared_ptr<A> >("A", boost::python::no_init)
    //... .def, etc
    ;

В general вы не должны передавать общие указатели по ссылке, поскольку, если ссылка на общий указатель недействительна, то ссылка, на которую указывает общий указатель, являетсятакже признан недействительным (поскольку получение ссылки на общий указатель не увеличивает счетчик ссылок на объект, на который указывает указатель).

Совершенно безопасно передавать объекты boost::shared_ptr вокруг и из Python, счетчики ссылок (Python и shared_ptr) будут правильно управляться, если вы не измените return_value_policy.Если вы измените политику метода, представленного в python, чтобы он возвращал ссылку на общий указатель, то вы можете вызвать проблемы, так же как передача общих указателей по ссылкам c ++ может вызвать проблемы.

(Кроме того, выследует использовать make_shared<A>(...) вместо shared_ptr<A>(new A(...)).)

1 голос
/ 27 июля 2010

В этой ситуации мой код будет выглядеть так (для вашего примера):

...

BOOST_PYTHON_MODULE(libp)
{
    class_<A, boost::shared_ptr<A>, boost::noncopyable >("A")
       .def("f", &A::f)
       .def("Count", &Count)
     ;
 } 

Важно запретить boost :: python копировать, но если вы используете Скорее всего, shared_ptr нужно копировать только в нескольких контролируемых ситуациях.

...