Разыменование указателя функции C внутри структуры в Python/ctypes

У меня есть следующая структура и объявление функции в C:

typedef int (*callback)(struct instance *);

typedef struct instance {
    int x;
    callback f;
} instance;

Каким будет правильный способ определить обратный вызов в Python с использованием ctypes?

Я пытаюсь объявить структуру в Python следующим образом:

class INSTANCE_STRUCT(ctypes.Structure):
       _fields_ = [("x", c_int),
                   ("f", c_void_p)]

Итак, в основном я использую c_void_p, чтобы объявить f как указатель void и хотел бы привести его к функции.

Я создаю структуру в исходном коде C в куче с помощью malloc, а затем в Python я обращаюсь к ней следующим образом:

instance = ctypes.cast(pointer_to_structure, ctypes.POINTER(INSTANCE_STRUCT))
print(instance.contents.x)
print(instance.contents.f)

Запуск скрипта дает мне следующий вывод:

Initializing struct x=[4] with function f() result=[8] // this happens in C and is correct
4 // value of instance.x
140027207110960 // address of instance.f (?)

Теперь, имея адрес instance.f(), я предполагаю, что мне нужно каким-то образом привести его к методу Python. Я пробовал это:

def CALLBACK_FUNC(self, structure):
     pass

callback = ctypes.cast(instance.contents.f, ctypes.POINTER(CALLBACK_FUNC))

Но он просто выдает ошибку:

Traceback (most recent call last):
  File "binding_test.py", line 19, in <module>
    callback = ctypes.cast(instance.contents.f, ctypes.POINTER(callback_function))
TypeError: must be a ctypes type

Кто-нибудь знает, как можно разыменовать функцию instance.f() в Python, учитывая, что функция обратного вызова должна иметь сам объект INSTANCE_STRUCT в качестве параметра?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
841
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Указатели функций могут использовать CFUNCTYPE(retval,params...) для объявления указателей функций C. Приведенный ниже код является предположением, основанным на описании и минимальном коде:

тест.с

#if defined(_WIN32)
#   define API __declspec(dllexport)
#else
#   define API
#endif

#include <stdlib.h>

struct instance; // forward declaration...

typedef int (*callback)(struct instance *); // so callback can be defined...

typedef struct instance { // and structure declared.
    int x;
    callback f;
} instance;

int func(instance* p) {  // Callback
    return ++p->x;
}

API instance* get_instance(void) {
    instance* p = malloc(sizeof(instance));
    p->x = 5;
    p->f = func;
    return p;
}

API void free_instance(instance* p) {
    free(p);
}

test.py

from ctypes import *

# Forward declaration...
class Instance(Structure):
    pass

# so the callback parameter pointer can be declared...
CALLBACK = CFUNCTYPE(c_int,POINTER(Instance))

# and then the fields can be defined.
Instance._fields_ = (('x',c_int),
                     ('f',CALLBACK))

dll = CDLL('./test')
dll.get_instance.argtypes = ()
dll.get_instance.restype = POINTER(Instance)
dll.free_instance.argtypes = POINTER(Instance),
dll.free_instance.restype = None

instance = dll.get_instance()
try:
    print(instance.contents.x)
    print(instance.contents.f(instance))
    print(instance.contents.f(instance))
    print(instance.contents.f(instance))
finally:
    dll.free_instance(instance)

Выход:

5
6
7
8

Использованная литература:

Это именно то, что я искал, спасибо!

Patryk Szczypień 15.12.2020 09:21

Другие вопросы по теме