【问题标题】:Dereferencing C function pointer inside a struct in Python/ctypes在 Python/ctypes 中的结构内取消引用 C 函数指针
【发布时间】:2020-12-14 18:08:27
【问题描述】:

我在 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_pf 声明为空指针,并希望将其转换为函数。

我正在使用 malloc 在堆上的 C 源代码中创建结构,然后在 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_STRUCT 对象本身作为参数,有人知道在 Python 中取消引用 instance.f() 函数的方法是什么吗?

【问题讨论】:

    标签: python c ctypes


    【解决方案1】:

    函数指针可以使用CFUNCTYPE(retval,params...) 来声明C 函数指针。下面的代码是根据描述和最小代码的猜测:

    test.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
    

    参考资料:

    【讨论】:

    • 这正是我想要的,谢谢!
    猜你喜欢
    • 2011-02-04
    • 2017-01-22
    • 2013-07-01
    • 2023-04-07
    • 1970-01-01
    • 2013-01-04
    • 2017-06-17
    • 2015-06-12
    相关资源
    最近更新 更多