2

I am trying to design class with callback function which is transferred to some C library. Need to grant access to object of this class without changing callback arguments. How to do this?

from ctypes import *
...
lib = CDLL('mylibname.so')
    ...
    class A(object):
        def __init__(self):
            CALLBACK = CFUNCTYPE(c_uint32, c_void_p)
            self.callback_func = CALLBACK(A.message_callback)
            self.param = None
        def message_callback(data):
            ... #here need access to self.param
            return 0
        def set_param(self, param):
            self.param = param
    ...
    a = A()
    lib.lib_func(param1, param2, a.callback_func)

EDIT: I've changed callback method in the class with wrapper function:

from ctypes import *
...
lib = CDLL('mylibname.so')

class struct_t(Structure):
    pass
struct_t._fields_ = [('next', POINTER(value_pair_t)),
                         ('key', c_char_p),
                         ('value', c_char_p)]
...
class A(object):
    def __init__(self):
        self.param = None

    def wrapper(self):
        CALLBACK = CFUNCTYPE(c_uint32, POINTER(struct_t))
        def message_callback(data):
            ... # now I have access to self here
            return 0
        return CALLBACK(message_callback)

    def set_param(self, param):
        self.param = param
...
a = A()
lib.lib_func(param1, param2, a.wrapper())

It works in python2, but I still have issues with python3:

Traceback (most recent call last): File "_ctypes/callbacks.c", line 260, in 'calling callback function' TypeError: 'LP_struct_t' object is not callable

Here is link with same issue: Weird bug?

2
  • Why would you create a closure to access self? Just use a method as the callable, as I showed you. Commented Dec 5, 2013 at 21:10
  • Your edit here also has a potential problem. The callable you pass to the C library only exists until lib_func returns. Then it will be garbage collected, deallocating the thunk code and data. If the thunk code still exists to call closure_fcn, then who knows what garbage p->callable points to. Apparently in this case it's the LP_struct_t. Commented Dec 5, 2013 at 21:41

1 Answer 1

3

Just define message_callback(self, data) and use self.callback_func = A.CALLBACK(self.message_callback). Note I used A.CALLBACK. Set it as a class attribute. Defining it for each instance is a waste of time.

For example:

C:

typedef int (*callback_t)(void *data);

int test(const char *data, callback_t f) {
    f((void *)data); 
    return 0;
}

Python:

from ctypes import *

class A(object):
    CALLBACK = CFUNCTYPE(c_uint32, c_void_p)

    def __init__(self):        
        self.callback_func = A.CALLBACK(self.message_callback)
        self.param = None

    def message_callback(self, data):
        self.param = cast(data, c_char_p).value
        return 0

Demo:

>>> lib = CDLL('./lib.so')
>>> a = A()
>>> lib.test.argtypes = [c_char_p, A.CALLBACK]
>>> lib.test("updated", a.callback_func)
0
>>> a.param
'updated'
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.