Как преобразовать управляемый буфер в вызываемый в Python - PullRequest
0 голосов
/ 07 июня 2018

Я написал Python Wrapper для библиотеки C ++ 14, используя SWIG.В C ++ API я могу зарегистрировать std :: functions как обратные вызовы.

У меня есть SWIG карта типов для std :: function для передачи лямбда-выражения, которое вызывает обратный вызов 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)

Когда я передаю лямбду непосредственно в функцию регистра,Я получаю следующую ошибку при вызове обратного вызова из C ++:

TypeError: 'managedbuffer' object is not callable

Почему вход PyObject $ не вызывается?Как разрешить оба выражения Python?

Пример кода:

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

1 Ответ

0 голосов
/ 10 июня 2018

Похоже, у вас есть проблема с подсчетом ссылок.Вам необходимо сохранить ссылку на $input даже после того, как карта типа std::function завершена.В противном случае будет пропущена ссылка после завершения вызова registerFunc.Самый простой способ сделать это - сделать так, чтобы ваша карта типов захватывала 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, но даже с обобщенный лямбда-захват , который останавливает возможность создания копии и вынуждает SWIG генерировать код без необходимости, это немного больше работы.

Я бы, вероятно, бросил бы хотя бы PyCallable_Check в карту типовхотя и для хорошей меры.

...