Python 3 ctypes调用需要通过另一个结构间接引用缓冲区的函数
我有一个C共享库,其中一个函数接受一个参数。 此参数是指向具有两个字段的结构的指针Python 3 ctypes调用需要通过另一个结构间接引用缓冲区的函数,python,python-3.x,ctypes,Python,Python 3.x,Ctypes,我有一个C共享库,其中一个函数接受一个参数。 此参数是指向具有两个字段的结构的指针 typedef struct { uint8_t *p_data; // Pointer to a fixed lenth buffer (100 bytes) uint16_t len; // number of valid bytes in the buffer (range 1-100) } data_t; 我需要在Pyt
typedef struct
{
uint8_t *p_data; // Pointer to a fixed lenth buffer (100 bytes)
uint16_t len; // number of valid bytes in the buffer (range 1-100)
} data_t;
我需要在Python 3脚本中设置一个100字节的缓冲区(我使用的是3.7.2/3.7.3),
加载库并调用此函数
int
fn_convert_buffer(data_t *data_p)
{
...
}
我的Python 3 ctypes调用尝试命中不兼容的类型
import ctypes as ct
# load the library, etc...
# lib_cdll = ct.CDLL(mySharedLib)
def c_py_fn_convert_buffer(b_p):
global lib_cdll
val = lib_cdll.fn_convert_buffer(ct.byref(b_p))
return int(val)
data_a = bytearray(100)
# Initialize the buffer with data.
uint8_p = ct.c_uint8 * len(data_a)
class BufferStruct_t (ct.Structure):
_pack_ = 1
_fields_ = [
("p_data", ct.POINTER(ct.c_uint8 * len(data_a))),
("len", ct.c_uint16)
]
data_buf = BufferStruct_t(uint8_p.from_buffer(data_a), ct.c_uint16(8))
# TypeError: incompatible types, c_ubyte_Array_100 instance
# instead of LP_c_ubyte_Array_100 instance
# Call C function in shared-library: int fn_convert_buffer(data_t *data_p);
z = c_py_fn_convert_buffer(data_buf)
我需要帮助理解我在上面的BufferStruct\t
定义中遗漏了什么。来自缓冲区的应该得到一个指向缓冲区的指针,但它似乎得到c\u ubyte\u数组\u 100
上的byref()
也不起作用
data_buf = BufferStruct_t(ct.byref(uint8_p.from_buffer(data_a)), ct.c_uint16(8))
# TypeError: expected LP_c_ubyte_Array_100 instance, got CArgObject
为了测试我的流程的基本情况,我制作了一个示例,分别发送缓冲区和长度参数
def c_py_fn_convert_data(d_p,l):
global lib_cdll
val = lib_cdll.fn_convert_data(ct.byref(d_p),ct.c_uint32(l))
return int(val)
test_a = ct.c_uint8 * len(data_a)
# Call C function in shared-library:
# int fn_convert_data(uint8_t *data_p, uint32_t length);
z = c_py_fn_convert_data(test_a.from_buffer(data_a), 8)
这个简化的案例有效
如何构建一个Python3对象,该对象包含对共享库函数所需缓冲区的引用
使用两种有效的变体进行更新。
更新1根据我后来读到的内容尝试了一个cast
(我不会轻易地抛出:-)
)
变了,
指向强制转换为引用特定长度数组的指针
data_buf = BufferStruct_t(cast(uint8_p.from_buffer(data_a),
ct.POINTER(ct.c_uint8 * len(data_a))),
ct.c_uint16(8))
根据马克的答案更新2。
将字段更改为
("p_data", ct.POINTER(ct.c_uint8 * len(data_a))),
到一个简单的指针形式
("p_data", ct.POINTER(ct.c_uint8)),
两种变体都有效。
然而,我很想知道这两种方法中哪一种更安全/正确处理ctypes。
使用数组形式是否更好?或者
使用简单的指针和独立发送的长度是否更好
您的结构定义声明了指向数组的指针,而不是C结构中的简单指针。下面是一个DLL的简单实现的工作示例,其中函数对数据求和:
test.c
#include <stdint.h>
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
typedef struct {
uint8_t *p_data;
uint16_t len;
} data_t;
API int fn_convert_buffer(data_t *data_p)
{
int i;
int sum = 0;
for(i = 0; i < data_p->len; ++i)
sum += data_p->p_data[i];
return sum;
}
输出:
15
我明白你的意思。我将尝试简单的指针方式与我的代码。我接受你的答案,因为是我的作品。用另一种对我有用的方法更新了我的问题(使用cast)。我很想知道,对于ctypes
@nik the cast works,哪种方法更安全/正确,但所包装的类型是uint8\u t*
,因此我的方法是正确的。你基本上是在铸造它,因为类型是错误的。您所拥有的内容表示C类型uint8\u t(*)[100]
,它不是如何声明的。如果您声明了正确的类型,则不需要强制转换(这正是您不应轻易强制转换的原因…通常是一个警告:有什么地方出错了:^)
import ctypes as ct
class BufferStruct_t(ct.Structure):
_pack_ = 1
_fields_ = [("p_data", ct.POINTER(ct.c_uint8)), # just a pointer
("len", ct.c_uint16)]
# Helper to initialize the data
def __init__(self,data):
self.p_data = (ct.c_uint8 * len(data))(*data)
self.len = len(data)
dll = ct.CDLL('test')
dll.fn_convert_buffer.argtypes = ct.POINTER(BufferStruct_t),
dll.fn_convert_buffer.restype = ct.c_int
data_buf = BufferStruct_t([1,2,3,4,5])
print(dll.fn_convert_buffer(data_buf))
15