Объекты Python как пользовательские данные в функциях обратного вызова ctypes

Функция C myfunc работает с большим объемом данных. Результаты возвращаются в куски функции обратного вызова:

int myfunc(const char *data, int (*callback)(char *result, void *userdata), void *userdata);

Используя ctypes, вам нечего вызывать myfunc из кода Python и возвращать результаты в функцию обратного вызова Python. Этот обратный вызов работает нормально.

myfunc = mylib.myfunc
myfunc.restype = c_int
myfuncFUNCTYPE = CFUNCTYPE(STRING, c_void_p)
myfunc.argtypes = [POINTER(c_char), callbackFUNCTYPE, c_void_p]

def mycb(result, userdata):
    print result
    return True

input="A large chunk of data."
myfunc(input, myfuncFUNCTYPE(mycb), 0)

Но есть ли способ передать объект Python (скажем, список) как userdata для функции обратного вызова? Чтобы сохранить куски результатов, я хотел бы сделать, например:

def mycb(result, userdata):
    userdata.append(result)

userdata=[]

Но я понятия не имею, как передать список Python в c_void_p, чтобы его можно было использовать в вызове myfunc.

Мое текущее решение - реализовать связанный список как структуру ctypes, что довольно громоздко.

Ответ 1

Я думаю, вы могли бы использовать Python C API для этого... возможно, вы могли бы использовать указатель PyObject.

edit. Как указано в комментариях к op, уже существует тип py_object, который легко доступен в ctypes, поэтому решение состоит в создании первого объекта ctypes.py_object из списка python и затем передав его в c_void_p, чтобы передать его как аргумент функции C (я думаю, что этот шаг может оказаться ненужным, поскольку параметр, введенный как void*, должен принимать любой указатель, и было бы быстрее передать только byref). В обратном вызове выполняются обратные шаги (вывод из указателя void на указатель на py_object, а затем получение значения содержимого).

Обходным решением может быть использование замыкания для вашей функции обратного вызова, чтобы он уже знал, в каком списке он должен добавлять элементы...

myfunc = mylib.myfunc
myfunc.restype = c_int
myfuncFUNCTYPE = CFUNCTYPE(STRING)
myfunc.argtypes = [POINTER(c_char), callbackFUNCTYPE]


def mycb(result, userdata):
    userdata.append(result)

input="A large chunk of data."
userdata = []
myfunc(input, myfuncFUNCTYPE(lambda x: mycb(x, userdata)))