ip0000h
ip0000h

Reputation: 168

Python: access to object attribute from callback

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?

Upvotes: 2

Views: 1922

Answers (1)

Eryk Sun
Eryk Sun

Reputation: 34270

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'

Upvotes: 3

Related Questions