如何将'managedbuffer'转换为Python中的Callable

时间:2018-06-07 14:39:37

标签: python c++ python-3.x c++11 swig

我使用SWIG为C ++ 14库编写了一个Python Wrapper。 在C ++ API中,我可以将std :: functions注册为回调。

我有一个用于std :: function的SWIG类型映射来传递lambda表达式 它调用Python回调:

%typemap(in) std::function {
    auto callback = [$input](auto&&... params) {
       PyGILState_STATE state = PyGILState_Ensure();

       PyObject* result =  PyObject_CallFunctionObjArgs($input,makePyObject(std::forward<decltype(params)>(params))..., NULL);
       const int retVal = PyObject_IsTrue(result);

       Py_DECREF(result);
       PyGILState_Release(state);
       return retVal == 1;
   };
   $1 = std::move(callback);
}

当我运行测试脚本时,以下Python表达式可以正常工作 细:

callback = lambda a,b: self.doStuff(a,b)
self.cppInterface.registerFunc(callback)

但是这个表达不起作用:

self.cppInterface.registerFunc(lambda a,b: self.doStuff)

当我将lambda直接传递给寄存器函数时, 从C ++调用回调时出现以下错误:

TypeError: 'managedbuffer' object is not callable

为什么PyObject $输入不可调用? 我如何允许两个Python表达式?

示例代码:

https://github.com/nullmedium/python-swig-demo

1 个答案:

答案 0 :(得分:1)

看起来你有引用计数问题。即使在$input类型映射完成后,您也需要保留对std::function的引用。否则,一旦完成对registerFunc的调用,它就会丢失引用。最简单的方法是使您的typemap捕获std::shared_ptr而不是原始PyObject,例如:

%typemap(in) std::function {
    Py_INCREF($input);
    static const auto decref = [](PyObject *o) {
        Py_DECREF(o); // This needs to be another lambda/function because Py_DECREF is really a macro
    };
    std::shared_ptr<PyObject> callable($input, decref);
    auto callback = [callable](auto&&... params) {
       PyGILState_STATE state = PyGILState_Ensure();
                                                      // Back to raw PyObject    
       PyObject* result =  PyObject_CallFunctionObjArgs(callable.get(),makePyObject(std::forward<decltype(params)>(params))..., NULL);
       int retVal = -1;

       if (result)
       {
           retVal = PyObject_IsTrue(result);
           Py_DECREF(result);
       }

       PyGILState_Release(state);
       return retVal == 1;
   };
   $1 = std::move(callback);
}

我会理想地使用std::unique_ptr,但即使使用generalised lambda captures也可以阻止复制构造,并强制SWIG生成代码而不需要更多工作。

我可能会在类型图中至少抛出一个PyCallable_Check以获得良好的衡量标准。