如何控制 std::function 的 pybind11 包装中的参数传递策略?

How to control argument passing policy in pybind11 wrapping of std::function?

我在 c++ 中有一个 class,我正在用 pybind11 包装到 python 中。 class 有一个 std::function,我想控制如何处理该函数的参数(如 return 值策略)。我只是找不到执行此操作的语法或示例...

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 的析构函数中放置了一个打印输出,据我所知,它从未被调用过。

我在 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);
}

好的,我想我明白了:

而不是 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));     
        }
   }