Как управлять политикой передачи аргументов в pybind11, обертывающем std :: function?

У меня есть класс на C ++, который я оборачиваю в python с помощью pybind11. В этом классе есть std :: function, и я хотел бы контролировать, как обрабатываются аргументы этой функции (например, политики возвращаемых значений). Я просто не могу найти синтаксис или примеры, чтобы сделать это ...

class N {
public:
   using CallbackType = std::function<void(const OtherClass*)>;
   N(CallbackType callback): callback(callback) { }
   CallbackType callback;

   void doit() {
      OtherClass * o = new OtherClass();
      callback(o);
   }
}

завернутый в

py::class_<OtherClass>(...standard stuff...);

py::class_<N>(m, "N")
   .def(py::init<N::CallbackType>(),
      py::arg("callback"));

У меня все работает: я могу сделать это на python:

def callback(o):
   dosomethingwith(o)

k = N(callback)

, но я хотел бы иметь возможность контролировать, что происходит при вызове callback(o); - в основном, будет ли python владеть обернутой переменной o или нет.


Я помещаю распечатку в деструктор OtherClass, и, насколько я могу судить, он никогда не вызывается.


person Colin    schedule 07.02.2020    source источник


Ответы (2)


Я не вижу в pybind11/functional ничего, что позволило бы вам изменить право собственности на параметры в точке вызова, поскольку struct func_wrapper используется локальная функция, поэтому не может быть специализированной. Вы можете сами предоставить другую оболочку, но в коде вы не можете знать, является ли обратный вызов функцией Python или связанной функцией C ++ (ну, технически вы можете, если эта связанная функция C ++ связана с pybind11, но вы не можете знать в целом). Если это функция C ++, то изменение владения Python в оболочке было бы неправильным, поскольку временный прокси-сервер может уничтожить объект, даже если его полезная нагрузка сохраняется с помощью обратного вызова C ++.

У вас есть контроль над реализацией class N? Причина в том, что при использовании std::shared_ptr все ваши проблемы владения автоматически исчезнут, независимо от того, является ли функция обратного вызова C ++ или Python и хранит ли она аргумент или нет. Будет работать так, расширяя ваш пример выше:

#include <pybind11/pybind11.h>
#include <pybind11/functional.h>

namespace py = pybind11;

class OtherClass {};

class N {
public:
   using CallbackType = std::function<void(const std::shared_ptr<OtherClass>&)>;
   N(CallbackType callback): callback(callback) { }
   CallbackType callback;

   void doit() {
      auto o = std::make_shared<OtherClass>();
      callback(o);
   }
};

PYBIND11_MODULE(example, m) {
    py::class_<OtherClass, std::shared_ptr<OtherClass>>(m, "OtherClass");

    py::class_<N>(m, "N")
       .def(py::init<N::CallbackType>(), py::arg("callback"))
       .def("doit", &N::doit);
}
person Wim Lavrijsen    schedule 08.02.2020

ОК, думаю, я разобрался:

Вместо std :: function используйте pybind11 :: function:

using CallbackType = pybind11::function

а потом

void doit(const OtherClass &input) {
        if (<I want to copy it>) {
            callback(pybind11::cast(input, pybind11::return_value_policy::copy));
        } else {
            callback(pybind11::cast(input, pybind11::return_value_policy::reference));     
        }
   }
person Colin    schedule 11.02.2020