Python Ctypes注册回调函数
Python Ctypes register callback functions
我 运行 使用 Python 和 ctypes 进入一些非常 st运行ge 的东西。我正在使用 Python 3.4.3。首先,项目的一些背景:
我已经从 C 代码编译了一个自定义 dll。我正在使用 ctypes 与 dll 交互。 C 库与一些自定义硬件接口。有时硬件会生成一个中断并将其传递给计算机上的 C 库。在CAPI中,有一个原型为void register_callback(int addr, void (*callback)(void))
的函数。我有一个回调函数指针数组,它们被初始化为 NULL。调用该函数时,index addr处的回调函数指针设置为callback,如:callbacks[addr] = callback;
.
当用户在 Python 中编程时,他们会实例化 类 中的对象,这些对象模拟不同的硬件部件(例如按钮或 RGB LED)。然后他们可以编写自定义回调函数并调用 button.register_callback(func)
(当然,假设他们有一个名为 button 的 Button 对象),这将调用 C 库中的 register_callback 函数。现在,当按下按钮并产生中断时,C 库将调用适当的回调函数(即 callbacks[addr]();
)。
现在,奇怪的是:
在 Python 中,我第一次尝试 Python 中的 register_callback 方法是这样的:
class Obj:
def __init__(self, name):
# Initialize stuff
def register_callback(self, func):
CB_T = ctypes.CFUNCTYPE(None)
cb_ptr = CB_T(func)
host_api.register_callback(self.addr, cb_ptr) # host_api is the loaded dll
主要是:
def cb1():
print("cb1")
def cb2():
print("cb2")
def main(argv):
# Initialization stuff
# Now create the objects and register the callbacks:
obj = Obj_module.Obj()
obj2 = Obj_module.Obj()
obj.register_callback(cb1)
obj2.register_callback(cb2)
while True:
pass
当我 运行 这个时,只有 "cb2" 被打印,不管我按的是哪个按钮。真正奇怪的是,当我切换注册回调的顺序时:
obj2.register_callback(cb2)
obj.register_callback(cb1)
只打印了 "cb1",不管我按的是什么按钮!在 C 库中,我验证(通过 printf)根据按钮设置和调用了不同的回调函数指针,但相同的函数指针被传递给 C register_callback 函数。
我可以通过在 register_callback 方法中添加一行来解决问题:
def register_callback(self, func):
CB_T = ctypes.CFUNCTYPE(None)
cb_ptr = CB_T(func)
(ctypes.cast(cb_ptr, ctypes.POINTER(ctypes.c_int)))
host_api.register_callback(self.addr, cb_ptr)
显然,将 cb_ptr 转换为 ctypes POINTER 解决了问题 - 传入了不同的函数指针,我成功地看到 "cb1" 或 "cb2" 打印出来,具体取决于按钮我按下了。
我的问题是,为什么?为什么在原始代码中传递了相同的函数指针,为什么它根据我注册回调的顺序而改变,为什么将 cb_ptr 转换为 ctypes POINTER 确保函数指针不同?
我是 Python 的初学者,但我在 C 方面更有经验。提前感谢您的回复。
您的 cb_ptr
正在被垃圾回收。来自 documentation:
Make sure you keep references to CFUNCTYPE() objects as long as they
are used from C code. ctypes doesn’t, and if you don’t, they may be
garbage collected, crashing your program when a callback is made.
在此代码示例中,如果行 ptrs.append(cb_ptr)
被注释掉,cb_ptr
的位置对于两个 Obj
实例(在我的计算机上)是相同的。取消注释该行会产生两个内存位置。
import ctypes
ptrs = []
class Obj:
def __init__(self):
pass
def register_callback(self, func):
CB_T = ctypes.CFUNCTYPE(None)
cb_ptr = CB_T(func)
ptrs.append(cb_ptr)
print(cb_ptr)
def cb1(): print("cb1")
def cb2(): print("cb2")
def main(argv):
obj = Obj()
obj2 = Obj()
obj.register_callback(cb1)
obj2.register_callback(cb2)
main(None)
我 运行 使用 Python 和 ctypes 进入一些非常 st运行ge 的东西。我正在使用 Python 3.4.3。首先,项目的一些背景:
我已经从 C 代码编译了一个自定义 dll。我正在使用 ctypes 与 dll 交互。 C 库与一些自定义硬件接口。有时硬件会生成一个中断并将其传递给计算机上的 C 库。在CAPI中,有一个原型为void register_callback(int addr, void (*callback)(void))
的函数。我有一个回调函数指针数组,它们被初始化为 NULL。调用该函数时,index addr处的回调函数指针设置为callback,如:callbacks[addr] = callback;
.
当用户在 Python 中编程时,他们会实例化 类 中的对象,这些对象模拟不同的硬件部件(例如按钮或 RGB LED)。然后他们可以编写自定义回调函数并调用 button.register_callback(func)
(当然,假设他们有一个名为 button 的 Button 对象),这将调用 C 库中的 register_callback 函数。现在,当按下按钮并产生中断时,C 库将调用适当的回调函数(即 callbacks[addr]();
)。
现在,奇怪的是:
在 Python 中,我第一次尝试 Python 中的 register_callback 方法是这样的:
class Obj:
def __init__(self, name):
# Initialize stuff
def register_callback(self, func):
CB_T = ctypes.CFUNCTYPE(None)
cb_ptr = CB_T(func)
host_api.register_callback(self.addr, cb_ptr) # host_api is the loaded dll
主要是:
def cb1():
print("cb1")
def cb2():
print("cb2")
def main(argv):
# Initialization stuff
# Now create the objects and register the callbacks:
obj = Obj_module.Obj()
obj2 = Obj_module.Obj()
obj.register_callback(cb1)
obj2.register_callback(cb2)
while True:
pass
当我 运行 这个时,只有 "cb2" 被打印,不管我按的是哪个按钮。真正奇怪的是,当我切换注册回调的顺序时:
obj2.register_callback(cb2)
obj.register_callback(cb1)
只打印了 "cb1",不管我按的是什么按钮!在 C 库中,我验证(通过 printf)根据按钮设置和调用了不同的回调函数指针,但相同的函数指针被传递给 C register_callback 函数。
我可以通过在 register_callback 方法中添加一行来解决问题:
def register_callback(self, func):
CB_T = ctypes.CFUNCTYPE(None)
cb_ptr = CB_T(func)
(ctypes.cast(cb_ptr, ctypes.POINTER(ctypes.c_int)))
host_api.register_callback(self.addr, cb_ptr)
显然,将 cb_ptr 转换为 ctypes POINTER 解决了问题 - 传入了不同的函数指针,我成功地看到 "cb1" 或 "cb2" 打印出来,具体取决于按钮我按下了。
我的问题是,为什么?为什么在原始代码中传递了相同的函数指针,为什么它根据我注册回调的顺序而改变,为什么将 cb_ptr 转换为 ctypes POINTER 确保函数指针不同?
我是 Python 的初学者,但我在 C 方面更有经验。提前感谢您的回复。
您的 cb_ptr
正在被垃圾回收。来自 documentation:
Make sure you keep references to CFUNCTYPE() objects as long as they are used from C code. ctypes doesn’t, and if you don’t, they may be garbage collected, crashing your program when a callback is made.
在此代码示例中,如果行 ptrs.append(cb_ptr)
被注释掉,cb_ptr
的位置对于两个 Obj
实例(在我的计算机上)是相同的。取消注释该行会产生两个内存位置。
import ctypes
ptrs = []
class Obj:
def __init__(self):
pass
def register_callback(self, func):
CB_T = ctypes.CFUNCTYPE(None)
cb_ptr = CB_T(func)
ptrs.append(cb_ptr)
print(cb_ptr)
def cb1(): print("cb1")
def cb2(): print("cb2")
def main(argv):
obj = Obj()
obj2 = Obj()
obj.register_callback(cb1)
obj2.register_callback(cb2)
main(None)