Предоставление функций 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)