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