在 Python/ctypes 中的结构内取消引用 C 函数指针
Posted
技术标签:
【中文标题】在 Python/ctypes 中的结构内取消引用 C 函数指针【英文标题】:Dereferencing C function pointer inside a struct in Python/ctypes 【发布时间】: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_p
将f
声明为空指针,并希望将其转换为函数。
我正在使用 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() 函数的方法是什么吗?
【问题讨论】:
【参考方案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
参考资料:
https://docs.python.org/3/library/ctypes.html#incomplete-types https://docs.python.org/3/library/ctypes.html#callback-functions【讨论】:
这正是我想要的,谢谢!以上是关于在 Python/ctypes 中的结构内取消引用 C 函数指针的主要内容,如果未能解决你的问题,请参考以下文章