Python 3 ctypes调用需要通过另一个结构间接引用缓冲区的函数

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

我有一个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
定义中遗漏了什么。来自缓冲区的
应该得到一个指向缓冲区的指针,但它似乎得到
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