mga*_*rey 2 python ctypes callback
我使用 Python 和 ctypes 遇到了一些非常奇怪的事情。我正在使用Python 3.4.3。首先,介绍一下该项目的一些背景:
我已经从 C 代码编译了一个自定义 dll。我正在使用 ctypes 与 dll 进行交互。C 库与一些定制硬件连接。有时,硬件会生成中断并将其传递给计算机上的 C 库。在C API中,有一个带有原型的函数void register_callback(int addr, void (*callback)(void))。我有一个回调函数指针数组,它们被初始化为 NULL。当这个函数被调用时,索引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
Run Code Online (Sandbox Code Playgroud)
主要是:
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
Run Code Online (Sandbox Code Playgroud)
当我运行此命令时,无论我按哪个按钮,都只打印“cb2”。真正奇怪的是,当我切换注册回调的顺序时:
obj2.register_callback(cb2)
obj.register_callback(cb1)
Run Code Online (Sandbox Code Playgroud)
无论我按下哪个按钮,都只打印“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)
Run Code Online (Sandbox Code Playgroud)
显然,将 cb_ptr 转换为 ctypes POINTER 解决了问题 - 传入不同的函数指针,并且我成功地看到打印了“cb1”或“cb2”,具体取决于我按下的按钮。
我的问题是,为什么?为什么在原始代码中传递相同的函数指针,为什么它会根据我注册回调的顺序而变化,以及为什么将 cb_ptr 转换为 ctypes POINTER 可确保函数指针不同?
我是 Python 的初学者,但我在 C 方面更有经验。提前感谢您的回复。
你的cb_ptr正在被垃圾收集。从文档中:
\n\n\n确保保留对 CFUNCTYPE() 对象的引用,只要它们是从 C 代码中使用的。ctypes 不会\xe2\x80\x99t,如果你不\xe2\x80\x99t,它们可能会被\n 垃圾回收,从而在进行回调时使你的程序崩溃。
\n
在此代码示例中,如果该行ptrs.append(cb_ptr)被注释掉,则两个实例(在我的计算机上)的位置cb_ptr是相同的。Obj取消注释该行会产生两个内存位置。
import ctypes\n\nptrs = []\n\nclass Obj:\n def __init__(self):\n pass\n\n def register_callback(self, func):\n CB_T = ctypes.CFUNCTYPE(None)\n cb_ptr = CB_T(func)\n ptrs.append(cb_ptr)\n print(cb_ptr)\n\ndef cb1(): print("cb1")\n\ndef cb2(): print("cb2")\n\ndef main(argv):\n obj = Obj()\n obj2 = Obj()\n obj.register_callback(cb1)\n obj2.register_callback(cb2)\n\nmain(None)\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
2603 次 |
| 最近记录: |