Как экспортировать shared_ptr фундаментального типа в python

0

Я пытаюсь использовать в python shared_ptr базового типа (например, int или double), но я не знаю, как его экспортировать в python:

У меня есть следующий класс:

class Holder
{
public:
    Holder(int v) : value(new int(v)) {};
    boost::shared_ptr<int> value;
};

Класс экспортируется таким образом:

class_<Holder>("Holder", init<int>())
    .def_readwrite("value", &Holder::value);

В коде python я пытаюсь установить "экземпляр владельца.value", используя экземпляр, который уже существует.

h1 = mk.Holder(10)
h2 = mk.Holder(20)
h1.value = h2.value

Произошло следующее:

TypeError: No to_python (by-value) converter found for C++ type: class boost::shared_ptr<int>

Мой вопрос: как я могу экспортировать boost::shared_ptr<int> в python?

  • 0
    Какая семантика вам нужна? Продолжая приведенный выше пример, ожидаете ли вы, что сможете присваивать целочисленные значения h1.value и h1.value ли они в h2.value ? Как насчет чего-то вроде этого x = h1.value; x = 42 ... должно ли h2.value как-то оцениваться до 42 после этого утверждения? То, что вы спрашиваете, немного против зерна в Python, но мы, вероятно, можем найти разумный ответ.
Теги:
boost-python

2 ответа

1
Лучший ответ

Можно использовать boost::python::class_ для экспорта boost::shared_ptr<int> в Python таким же образом, как и другие типы:

boost::python::class_<boost::shared_ptr<int> >(...);

Однако будьте осторожны в семантике, введенной при разоблачении shared_ptr. Например, рассмотрим случай, когда присвоение одного значения Holder.value другому Holder.value просто вызывает оператор boost::shared_ptr<int> присваивания, а Holder.increment() манипулирует значением int указывает value а не имеет value указывающее на новый int:

h1 = Holder(10)
h2 = Holder(20)
h1.value = h2.value # h1.value and h2.value point to the same int.
h1.increment() # Change observed in h2.value.  The semantics may be unexpected
               # by Python developers.

Вот полный пример, демонстрирующий boost::shared_ptr<int> на основе исходного кода:

#include <sstream>
#include <string>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>

class Holder
{
public:
  Holder(int v)
    : value(new int(v))
  {};

  boost::shared_ptr<int> value;
};

std::string holder_value_str(const boost::shared_ptr<int>& value)
{
  std::stringstream stream;
  stream << value.get() << " contains " << *value;
  return stream.str();
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  {
    python::scope holder = python::class_<Holder>(
        "Holder", python::init<int>())
      .def_readwrite("value", &Holder::value)
      ;

    // Holder.Value
    python::class_<boost::shared_ptr<int> >("Value", python::no_init)
      .def("__str__", &holder_value_str)
      ;
  }
}

Интерактивное использование:

>>> import example
>>> h1 = example.Holder(10)
>>> h2 = example.Holder(20)
>>> print h1.value
0x25f4bd0 contains 10
>>> print h2.value
0x253f220 contains 20
>>> h1.value = h2.value
>>> print h1.value
0x253f220 contains 20

В качестве альтернативы, если рассматривать shared_ptr как не что иное, как прокси-сервер управления C++, тогда может быть разумным выставить Holder.value как int в Python, хотя Holder::value является boost::shared_ptr<int>. Это обеспечило бы разработчикам Python ожидаемую семантику и разрешительные утверждения, такие как h1.value = h2.value + 5. Вот пример, который использует вспомогательные функции для преобразования Holder::value в и из int вместо того, чтобы подвергать boost::shared_ptr<int> type:

#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>

class Holder
{
public:
  Holder(int v)
    : value(new int(v))
  {};

  boost::shared_ptr<int> value;
};

/// @brief Auxiliary function used to get Holder.value.
int get_holder_value(const Holder& self)
{
  return *(self.value);
}

/// @brief Auxiliary function used to set Holder.value.
void set_holder_value(Holder& self, int value)
{
  *(self.value) = value;
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::scope holder = python::class_<Holder>(
      "Holder", python::init<int>())
    .add_property("value",
      python::make_function(&get_holder_value),
      python::make_function(&set_holder_value))
    ;
}

Интерактивное использование:

>>> import example
>>> h1 = example.Holder(10)
>>> h2 = example.Holder(20)
>>> assert(h1.value == 10)
>>> assert(h2.value == 20)
>>> h1.value = h2.value
>>> assert(h1.value == 20)
>>> h1.value += 22
>>> assert(h1.value == 42)
>>> assert(h2.value == 20)
1

Вам нужно? У Python есть свой собственный механизм подсчета ссылок, и проще было бы это использовать. (Но многое зависит от того, что происходит на стороне C++.)

Иначе: вам, вероятно, нужно определить объект Python, чтобы он содержал общий указатель. Это относительно просто: просто определите что-то вроде:

struct PythonWrapper
{
    PyObject_HEAD
    boost::shared_ptr<int> value;
    //  Constructors and destructors can go here, to manage
    //  value.
};

И объявляйте и управляйте им, как и любой другой объект; просто убедитесь, что вы делаете new когда когда-либо создаются объекты типа (и они должны создаваться в функциях, которые вы предоставляете Python, если ничего больше в функции в поле tp_new) и delete в функции, которую вы помещаете в tp_dealloc поля объекта типа, который вы регистрируете.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню