Automati c понижение возвращаемого значения с использованием общего указателя в boost python - PullRequest
0 голосов
/ 25 апреля 2020

Я хотел бы знать, есть ли способ выставить «пониженный» объект в результате функции в python, используя boost::python, используя общие указатели. Рассмотрим следующий сценарий:

class A {
    virtual ~A() { }
};

class B: public A {
    std::shared_ptr<B> makeB() { return std::make_shared<B>(); }
    std::shared_ptr<A> f(bool b) { return b ? std::make_shared<B>() : std::make_shared<A>();
};


BOOST_PYTHON_MODULE(module) {

    using namespace boost::python;

    register_ptr_to_python<std::shared_ptr<A>>();
    register_ptr_to_python<std::shared_ptr<B>>();

    class_<A, std::shared_ptr<A>, boost::noncopyable>("A", no_init);

    class_<B, bases<A>, boost::noncopyable>("B", no_init)
        .def("f", &B::f)
        .def("makeB", &B::makeB).staticmethod("makeB");

}

Теперь в python я могу получить объект B и вызвать f, но полученный объект будет отображаться как объект A:

import module

b = module.B.makeB()
type(b)  # This is module.B

u = b.f(true)
type(u)  # This is always module.A

В любом случае, чтобы динамически возвращать объект типа B на стороне python? В C ++ мне приходится вручную понижать, что нормально, потому что это распространено в C ++, но в python я был бы признателен за тип возвращаемого динамического c.

1 Ответ

0 голосов
/ 25 апреля 2020

Так что мне удалось заставить это работать, используя конвертер результатов :

template <class FromType, class ToType>
struct DowncastReturn {

  template <class T>
  struct apply;

  template <>
  struct apply<std::shared_ptr<FromType>> {
    struct type {

      bool convertible() const { return true;  }

      inline PyObject* operator()(std::shared_ptr<FromType> p) const {
        if (p == nullptr) {
          return bpy::detail::none();
        }
        else {
          auto downcast_p = std::dynamic_pointer_cast<ToType>(p);
          bpy::object p_value = downcast_p == nullptr ? bpy::object{ p } : bpy::object{ downcast_p };
          return bpy::incref(p_value.ptr());
        }
      }

      inline PyTypeObject const* get_pytype() const {
        return bpy::converter::registered_pytype<FromType>::get_pytype();
      }

    };
  };
};

Тогда это можно использовать:

class_<B, bases<A>, boost::noncopyable>("B", no_init)
        .def("f", &B::f, return_value_policy<DowncastReturn<A, B>>())
        .def("makeB", &B::makeB).staticmethod("makeB");

Это требует немного больше для настройки, чем моя первая версия, но гораздо проще в использовании, если это требуется для нескольких методов.


Первая версия:

Это первая версия моего ответа, где я сделал Вниз вручную, не полагаясь на return_value_policy. Идея заключалась в том, чтобы просто вручную вернуть возвращаемый объект в boost::python::object:

class_<B, bases<A>, boost::noncopyable>("B", no_init)
        .def("f", +[](B *self, bool b) { 
            auto *r = self->f();
            auto *rb = dynamic_cast<B*>(r);  
            return rb == nullptr ? object{ r } : object{ rb };
        })
        .def("makeB", &B::makeB).staticmethod("makeB");
...