[英]Python Ctypes register callback functions
我使用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
而在主要方面:
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
正在被垃圾回收。 從文檔中 :
確保保留對CFUNCTYPE()對象的引用,只要它們是從C代碼中使用的即可。 ctypes不會,如果您不這樣做,則可能會被垃圾回收,從而使您的程序在進行回調時崩潰。
在此代碼示例中,如果注釋掉了ptrs.append(cb_ptr)
行,則兩個Obj
實例的cb_ptr
位置都相同(在我的計算機上)。 取消注釋該行將導致兩個存儲位置。
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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.