Python 3 ctypes 调用需要通过另一个结构间接引用缓冲区的函数
Python 3 ctypes call to a function that needs an indirect reference to a buffer through another structure
我有一个 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;
我需要在我的 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
定义中遗漏的内容。 from_buffer
应该得到一个指向缓冲区的指针,但它似乎得到 c_ubyte_ARRAY_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)
这个简化案例有效。
如何构建一个 Python 3 对象,该对象携带对共享库函数期望的缓冲区的引用?
更新两个有效的变体。
更新 1 尝试了一个 cast
基于我后来读到的东西(我不会轻易投射 :-)
)
已更改,
data_buf = BufferStruct_t(uint8_p.from_buffer(data_a), ct.c_uint16(8))
指向一个指向特定长度数组的指针,
data_buf = BufferStruct_t(cast(uint8_p.from_buffer(data_a),
ct.POINTER(ct.c_uint8 * len(data_a))),
ct.c_uint16(8))
根据 Mark 的回答更新 2。
从
更改为 _field_
("p_data", ct.POINTER(ct.c_uint8 * len(data_a))),
到一个简单的指针形式,
("p_data", ct.POINTER(ct.c_uint8)),
两种变体都有效。
不过,我很想知道这两种方式中哪一种更能处理 safe/correct 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;
}
test.py
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
我有一个 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;
我需要在我的 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
定义中遗漏的内容。 from_buffer
应该得到一个指向缓冲区的指针,但它似乎得到 c_ubyte_ARRAY_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)
这个简化案例有效。
如何构建一个 Python 3 对象,该对象携带对共享库函数期望的缓冲区的引用?
更新两个有效的变体。
更新 1 尝试了一个 cast
基于我后来读到的东西(我不会轻易投射 :-)
)
已更改,
data_buf = BufferStruct_t(uint8_p.from_buffer(data_a), ct.c_uint16(8))
指向一个指向特定长度数组的指针,
data_buf = BufferStruct_t(cast(uint8_p.from_buffer(data_a),
ct.POINTER(ct.c_uint8 * len(data_a))),
ct.c_uint16(8))
根据 Mark 的回答更新 2。 从
更改为_field_
("p_data", ct.POINTER(ct.c_uint8 * len(data_a))),
到一个简单的指针形式,
("p_data", ct.POINTER(ct.c_uint8)),
两种变体都有效。
不过,我很想知道这两种方式中哪一种更能处理 safe/correct 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;
}
test.py
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