Предоставление функций C++, которые возвращают указатель с помощью Boost.Python
Я хочу представить следующую функцию C++ для Python, используя Boost.Python:
int* test1() {
return new int(42);
}
// Now exposing the function with Boost.Python
BOOST_PYTHON_MODULE(libtest1)
{
using namespace boost::python;
def("test1", test1);
}
Когда я пытаюсь скомпилировать эту библиотеку, происходит ошибка из-за (я думаю) Boost.Python не знаю, как конвертировать int* в PyObject.
Я думаю, что нужно сделать, это определить структуру преобразования, что-то вроде этого:
template<class T>
struct int_ptr_to_python
{
static PyObject* convert(int* i_ptr)
{
return i_ptr;
}
};
И передайте его объявлению BOOST_PYTHON_MODULE:
BOOST_PYTHON_MODULE(libtest1)
{
using namespace boost::python;
def("test1", test1);
to_python_converter<int*, int_ptr_to_python<int*> >();
}
Но это также не работает. И я не могу найти информацию о том, как должны обрабатываться функции, возвращающие указатели.
Кто-нибудь может помочь?
1 ответ
Короче говоря, нельзя напрямую выставить функцию, возвращающую int*
с Boost.Python, так как в Python нет значимого соответствующего типа, данные целые числа неизменны.
- Если цель состоит в том, чтобы вернуть число в Python, а затем вернуть
int
по значению. Это может потребовать использования вспомогательной функции для адаптации устаревших API. - Если цель состоит в том, чтобы вернуть указатель на объект, выделенный с помощью нового выражения, то этот объект должен быть классом или объединением, а функция должна быть представлена специальными политиками для указания ответственности владельцев.
Вместо того, чтобы сразу представлять окончательное решение, я хотел бы потратить время на то, чтобы разобраться с ошибками компилятора. В Boost.Python иногда для предоставления инструкций в сообщениях компилятора используются статические утверждения до C++11. К сожалению, может быть немного трудно найти их среди тяжелых шаблонов.
Следующий код:
#include <boost/python.hpp>
int* make_int() { return new int(42); }
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("make_int", &make_int);
}
производит следующий соответствующий вывод в clang, с важными деталями, выделенными жирным шрифтом:
... / boost / python/ detail / caller.hpp: 102: 98: ошибка: нет члена с именем 'get_pytype' в 'boost::python::detail:: define_a_return_value_policy_to_wrap_functions_returning'... create_result_converter ((PyObject *) 0, (ResultConverter *) 0, (ResultConverter *) 0).g...
Boost.Python сообщает нам, что boost::python::return_value_policy
необходимо указать для возврата функций int*
, Существуют различные модели ResultConverterGenerators. Часто политики используются для управления владением или семантикой времени жизни возвращаемого объекта. Поскольку функция в исходном коде возвращает новый указатель непосредственно в Python, boost::python::manage_new_object
подходит, если ожидается, что вызывающая сторона будет нести ответственность за удаление объекта.
Задать политику для управления объектами все еще не удается:
#include <boost/python.hpp>
int* make_int() { return new int(42); }
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("make_int", &make_int,
python::return_value_policy<python::manage_new_object>());
}
производит следующий соответствующий вывод:
... / импульс / Python/ объект /make_instance.hpp:27:9: ошибка: нет подходящей функции для вызова 'assertion_failed' BOOST_MPL_ASSERT ((mpl:: or_, is_union ;>))
В этом случае Boost.Python информирует нас о том, что объект вернулся из функции с managed_new_object
ResultConverterGenerator должен быть либо class
или же union
, Для int*
самое подходящее решение - вернуть int
по значению при прохождении через слой Boost.Python. Однако для полноты ниже демонстрируется:
- Использование вспомогательной функции для адаптации устаревшего API.
- Как ограничить создание пользовательского типа с помощью фабричной функции, которая возвращает указатели.
#include <boost/python.hpp>
/// Legacy API.
int* make_int() { return new int(42); }
/// Auxiliary function that adapts the legacy API to Python.
int py_make_int()
{
std::auto_ptr<int> ptr(make_int());
return *ptr;
}
/// Auxiliary class that adapts the legacy API to Python.
class holder
: private boost::noncopyable
{
public:
holder()
: value_(make_int())
{}
int get_value() const { return *value_; }
void set_value(int value) { *value_ = value; }
private:
std::auto_ptr<int> value_;
};
/// Factory function for the holder class.
holder* make_holder()
{
return new holder();
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("make_int", &py_make_int);
python::class_<holder, boost::noncopyable>("Holder", python::no_init)
.add_property("value",
python::make_function(&holder::get_value),
python::make_function(&holder::set_value))
;
python::def("make_holder", &make_holder,
python::return_value_policy<python::manage_new_object>());
}
Интерактивное использование:
>>> import example
>>> assert(42 == example.make_int())
>>> holder = example.Holder()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: This class cannot be instantiated from Python
>>> holder = example.make_holder()
>>> assert(42 == holder.value)
>>> holder.value *= 2
>>> assert(84 == holder.value)