Я пытаюсь использовать в 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?
Можно использовать 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)
Вам нужно? У 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
поля объекта типа, который вы регистрируете.
h1.value
иh1.value
ли они вh2.value
? Как насчет чего-то вроде этогоx = h1.value; x = 42
... должно лиh2.value
как-то оцениваться до42
после этого утверждения? То, что вы спрашиваете, немного против зерна в Python, но мы, вероятно, можем найти разумный ответ.