Я пытаюсь использовать PyBindGen для создания модуля расширения Python для моей библиотеки C, которая имеет обратный вызов.Даже если на главной странице PyBindGen сказано, что обратные вызовы являются отсутствующей функцией, в текущем исходном коде и в этом обсуждении и в архиве есть примеры того, как настроить PyBindGen для передачи обратного вызовафункция из питона в библиотеку C.
Проблема заключается в том, что в примере метод обратного вызова определяется как PyObject*
в качестве аргумента void *
для переноса информации указателя функции обратного вызова.Поскольку мой API не имеет такого указателя контекста, я пытаюсь сохранить ссылку на обратный вызов внутри кода привязок C , как в стандартном примере Python .Проблема в том, что я не могу понять, как заставить PyBindGen установить глобальный PyObject * my_callback.Как я могу это сделать?Вот мой код
Заголовочный файл моей библиотеки
typedef void (*CallbackType) (int value);
void register_cb(CallbackType cb);
void set_num(int n);
Исходный файл моей библиотеки
#include "c.h"
static CallbackType cb_;
void register_cb(CallbackType cb)
{
cb_ = cb;
}
void set_num(int n)
{
cb_(n);
}
Мой PyBindGen modulegen.py с закомментированным разделом о том, что нужнодля добавления к автоматически сгенерированному коду, чтобы он работал
import sys
import pybindgen
from pybindgen import ReturnValue, Parameter, Module, Function, FileCodeSink
from pybindgen import CppMethod, CppConstructor, CppClass, Enum
from pybindgen.typehandlers.base import ForwardWrapperBase
class CallbackTypeParam(Parameter):
DIRECTIONS = [Parameter.DIRECTION_IN]
CTYPES = ['CallbackType']
def convert_python_to_c(self, wrapper):
assert isinstance(wrapper, ForwardWrapperBase)
py_cb = wrapper.declarations.declare_variable("PyObject*", self.name)
wrapper.parse_params.add_parameter('O', ['&'+py_cb], self.name)
wrapper.before_call.write_error_check("!PyCallable_Check(%s)" % py_cb, """PyErr_SetString(PyExc_TypeError, "CallbackType parameter must be callable");""")
#####
# NEED TO INSERT THE FOLLOWING TWO LINES INTO AUTOGENERATED OUTPUT HERE
# Py_XDECREF(my_callback); // NEEDS TO BE AUTO-GENERATED !!!
# my_callback = cb; // NEEDS TO BE AUTO-GENERATED !!!
#####
wrapper.call_params.append("_wrap_callback")
wrapper.before_call.write_code("Py_INCREF(%s);" % py_cb)
wrapper.before_call.add_cleanup_code("Py_DECREF(%s);" % py_cb)
def convert_c_to_python(self, wrapper):
raise NotImplementedError
def my_module_gen(out_file):
mod = Module('c')
mod.add_include('"c.h"')
mod.header.writeln("""
void _wrap_callback(int value);
static PyObject *my_callback = NULL;
""")
mod.body.writeln("""
void _wrap_callback(int value)
{
int arg;
PyObject *arglist;
arg = value;
printf("@@@@ Inside the binding: %d %p\\n", value, my_callback); fflush(NULL);
arglist = Py_BuildValue("(i)", arg);
PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
}
""")
mod.add_function("register_cb", None, [Parameter.new("CallbackType", "cb")])
mod.add_function("set_num", None, [Parameter.new("int", "n")])
mod.generate(FileCodeSink(out_file))
if __name__ == '__main__':
my_module_gen(sys.stdout)
Мой файл setup.py для сборки модуля расширения
#!/usr/bin/env python
import os
from distutils.core import setup, Extension
from modulegen import my_module_gen as generate
try:
os.mkdir("build")
except OSError:
pass
os.environ["CC"] = "g++"
module_fname = os.path.join("build", "autogen-binding.c")
with open(module_fname, "wt") as file_:
print("Generating file {}".format(module_fname))
generate(file_)
mymodule = Extension('c',
sources = [module_fname, 'c.cc'],
include_dirs=['.'])
setup(name='PyBindGen-example',
version="0.0",
description='PyBindGen example',
author='xxx',
author_email='yyy@zz',
ext_modules=[mymodule],
)
Мой скрипт на Python с использованием модуля расширения
import c
def my_callback(value):
print("In Callback: " + str(value))
c.register_cb(my_callback)
c.set_num(10);
c.set_num(20);