Предоставление функций C++, которые возвращают указатель с помощью Boost.Питон
Я хочу открыть следующую функцию 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 ответов
короче говоря, нельзя напрямую выставлять функцию returning int*
С Boost.Python, поскольку в Python нет значимого соответствующего типа, заданные целые числа неизменяемы.
- если цель состоит в том, чтобы вернуть число в Python, то return
int
по значению. Это может потребовать использования вспомогательной функции для адаптации устаревших API. - если цель состоит в том, чтобы вернуть указатель на объект, выделенный с новым выражением, то объект должен быть классом или объединением, и функция должна быть выставлена с конкретными политиками, чтобы указать ответственность владельца.
вместо того, чтобы сразу представить окончательное решение, я хотел бы найти время, чтобы пройти через ошибки компилятора. С Boost.Python, иногда статические утверждения pre-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: error: no member named 'get_pytype' in 'boost::python::detail:: specify_a_return_value_policy_to_wrap_functions_returning<int*>' ...create_result_converter((PyObject*)0, (ResultConverter *)0, (ResultConverter *)0).g...
импульс.Python сообщает нам, что a 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>());
}
производит следующий соответствующий выход:
.../boost/python/object/make_instance.hpp:27:9: error: no matching function for call to 'assertion_failed' BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >));
в этом случае, 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)