Предоставление ускорения :: необязательно ‹T› через Boost.Python в качестве внутренней ссылки или None

Я показываю свои классы C ++ через Boost.Python. Я намерен предоставить переменные-члены определенного пользователем типа класса с внутренней ссылкой. Это работало нормально, пока я не решил ввести переменную-член типа boost :: optional ‹T›.

Есть несколько замечательных сообщений, которые показывают, как выставить boost :: optional ‹T› как возврат по значению. В частности, я реализовал этот конвертер. Другие соответствующие фрагменты моего кода выглядят так:

struct Bar {}

struct Foo {
  boost::optional<Bar> bar;
}

BOOST_PYTHON_MODULE(mymodule) {
  using namespace boost::python;

  python_optional<Bar>();  //registering the converter

  class_<Foo>("Foo")
    .add_property ( "bar", make_getter(&Foo::bar, return_value_policy<return_by_value>()), make_setter(&Foo::bar) )
  ;
}

Я попытался заменить return_value_policy<return_by_value>() на return_value_policy<reference_existing_object>() или return_internal_reference<>(). Оба вызвали ошибку Python TypeError:

>> import mymodule
>> foo = mymodule.Foo()
>> bar = foo.bar
TypeError: No Python class registered for C++ class boost::optional<Bar>

Насколько я понимаю, теперь я получаю ссылку на объект boost :: optional ‹T›. Однако зарегистрированный мною преобразователь не вызывается, потому что он ожидает объект boost :: optional ‹T›, а не ссылку на такой объект. Я думал о замене конвертера, но я новичок в этом и действительно не знаю, как это сделать. Какие-либо предложения?


person rocketeer    schedule 20.09.2017    source источник


Ответы (1)


Я нашел обходной путь, добавив геттер к struct Foo, который либо возвращает указатель на объект, удерживаемый boost :: optional, либо nullptr в случае boost :: none. Поскольку &*bar возвращает const Bar*, мне пришлось использовать const_cast.

struct Bar {}

struct Foo {
  Bar* getBar() { return (bar ? const_cast<Bar*>(&*bar) : nullptr); };
  boost::optional<Bar> bar;
}

BOOST_PYTHON_MODULE(mymodule) {
  using namespace boost::python;

  python_optional<Bar>();  //registering the converter

  class_<Foo>("Foo")
    .add_property ( "bar", make_function(static_cast< Bar*(Foo::*)() >(&Foo::getBar), return_internal_reference<>() ), make_setter(&Foo::bar) )
  ;
}

В случае, если bar принадлежит базовому классу Foo, Python выдаст ArgumentError следующим образом:

>> import mymodule
>> foo = mymodule.Foo()
>> foo.bar = mymodule.Bar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument tpyes in
  None.None(Foo, Bar)
did not match C++ signature:
  None(FooBase {lvalue}, boost::optional<Bar>)

Чтобы решить эту проблему, определите геттер и сеттер для bar в классе Foo.

struct Bar {}

struct FooBase {
  boost::optional<Bar> bar;
}

struct Foo : public FooBase {
  Bar* getBar() { return (bar ? const_cast<Bar*>(&*bar) : nullptr); };
  void setBar(const Bar* bar) { bar ?  this->bar = *bar : this->bar = boost::none; }
}

void (Foo::*foo_bar_set)(const Bar*) = &Foo::setBar;

BOOST_PYTHON_MODULE(mymodule) {
  using namespace boost::python;

  python_optional<Bar>();  //registering the converter

  class_<Foo>("Foo")
    .add_property ( "bar", make_function(static_cast< Bar*(Foo::*)() >(&Foo::getBar), return_internal_reference<>() ), foo_bar_set )
  ;
}
person rocketeer    schedule 21.09.2017