Reputation: 2167
Not sure what I am asking here is a proper way to do it (can't find any similar question), but here goes.
I need to initiate a callback function with custom arguments alongside with the ctypes arguments.
The Function
def initMessageCallback(myData):
callback = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_long) # return type and output parameter
lib.SetMessageCallback.restype = ctypes.c_bool
lib.SetMessageCallback(callback(callbackFunc))
Callback Function
I can access the parameter returned by SetMessageCallback
but how can I pass myData
during initMessageCallback
so that I can access it inside callbackFunc
without making it a global variable?
def callbackFunc(ID):
# need to access myData here
Upvotes: 1
Views: 2160
Reputation: 177745
There's a couple ways to do this. Your callback could be a method in a class and can access the instance data of the instantiated class, or you can just attach the data to the callback itself. Here's an example of the latter and some sample callback code:
test.c
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
typedef int (*CALLBACK)(int);
CALLBACK g_callback;
API void set_callback(CALLBACK cb)
{
g_callback = cb;
}
API int do_callback()
{
int sum = 0;
for(int i = 0; i < 5; ++i) // Call callback with 0,1,2,3,4 parameters.
if(g_callback)
sum += g_callback(i); // Collect and sum the callback return values.
return sum;
}
test.py
from ctypes import *
CALLBACK = CFUNCTYPE(c_int,c_int)
dll = CDLL('test')
dll.set_callback.argtypes = CALLBACK,
dll.set_callback.restype = c_int
dll.do_callback.argtypes = ()
dll.do_callback.restype = c_int
@CALLBACK
def my_callback(id):
return id + my_callback.my_data # Use the extra callback data.
def init_callback(my_data):
my_callback.my_data = my_data # Simply attach the data as a variable of the callback.
dll.set_callback(my_callback)
init_callback(0)
print(dll.do_callback()) # Expect (0+0)+(1+0)+(2+0)+(3+0)+(4+0) = 10
init_callback(1)
print(dll.do_callback()) # Expect (0+1)+(1+1)+(2+1)+(3+1)+(4+1) = 15
Output
10
15
A note about your sample code. callback(callbackFunc)
is created and passed to lib.SetMessageCallback()
but since no reference exists after that line executes the callback is destroyed. That can crash your system when you try to use the callback. Using a decorator (@CALLBACK) against the callback function like I've done above is the equivalent of my_callback = CALLBACK(my_callback)
. The function name is at the global level and won't go out of scope. So keep that in mind when generating callbacks. Store them in a variable and make sure it stays in scope until you're done with it.
Upvotes: 1