【问题标题】:ctypes: How to access array of structure returned by function?ctypes:如何访问函数返回的结构数组?
【发布时间】:2022-01-17 00:21:20
【问题描述】:

我有一个 c++ API 函数,我需要使用 ctypes 从 python 调用它。
在我的 c++ libamo.h 中,我有 structfunction 的原型,如下所示,

typedef struct contain_t
{
    uint8_t id;
    uint16_t ele1;
    uint16_t ele2;
    uint16_t ele3;
    uint16_t ele4;
    float ele5;
} mycontain;
mycontain* get_result(void *context, int r, int c, unsigned char* rawdata);

在我的 c++ libamo.cpp 中, 我已经声明了结构的全局数组,

mycontain all_contain[50];

函数mycontain* get_result() 填充结构数组,我已经在 c++ 中通过打印结构的内容对其进行了测试。

ctypes:

  • 正在加载libamo.so
  • 将结构模板定义为,
from ctypes import *
class mycontain(Structure):
    _fields_ = [('id', c_uint),
                ('ele1',c_uint),
                ('ele2', c_uint),
                ('ele3', c_uint),
                ('ele4', c_uint), 
                ('ele5', c_float) ]

ptr_cnt = POINTER(mycontain)
amo_get_result = libamo.get_result
amo_get_result.restype = ptr_cnt
amo_get_result.argtypeps = [c_void_p, c_int, c_int, c_char_p]

res = amo_get_result(amo_context, 300, 300, raw_val.ctypes.data_as(c_char_p))

我尝试了以下方法从结构成员中获取数据。
方法一:

output_res = res.contents
print(output_res.id, output_res.ele1, output_res.ele2, output_res.ele3, output_res.ele4, output_res.ele5)

对于上述元素,我得到的输出

7208960 0.0 4128919 173 1049669215 21364736

方法2:尝试投射

print(cast(output_res.id, POINTER(c_uint)))

output>><__main__.LP_c_uint object at 0x7f9450f3c0>

我的问题是, - 如何优雅地从结构数组中读取数据。我参考了多个 SO 帖子,大多数讨论的是访问单个结构实例的方法,而不是结构数组。

【问题讨论】:

    标签: python c++ ctypes


    【解决方案1】:

    在结构中使用匹配的类型。 c_uint 通常是 32 位的,因此您的 Python 结构的大小错误。

    要访问数组,请索引指针(例如output_res[0].id)或使用切片。

    这是一个带有测试 DLL 的可重现示例:

    test.cpp

    #include <stdint.h>
    
    #ifdef _WIN32
    #   define API __declspec(dllexport)
    #else
    #   define API
    #endif
    
    typedef struct contain_t
    {
        uint8_t id;
        uint16_t ele1;
        uint16_t ele2;
        uint16_t ele3;
        uint16_t ele4;
        float ele5;
    } mycontain;
    
    mycontain all_contain[5];
    
    extern "C"
    API mycontain* get_result(void *context, int r, int c, unsigned char* rawdata) {
        for(int i = 0; i < 50; ++i) {
            all_contain[i].id = i;
            all_contain[i].ele1 = i*2;
            all_contain[i].ele2 = i*3;
            all_contain[i].ele3 = i*4;
            all_contain[i].ele4 = i*5;
            all_contain[i].ele5 = i * 1.01;
        }
        return all_contain;
    }
    

    test.py

    from ctypes import *
    
    class mycontain(Structure):
        _fields_ = [('id', c_uint8),    # use correct types
                    ('ele1',c_uint16),
                    ('ele2', c_uint16),
                    ('ele3', c_uint16),
                    ('ele4', c_uint16), 
                    ('ele5', c_float) ]
    
        # Make a display representation for easy viewing
        def __repr__(self):
            return f'mycontain(id={self.id}, ele1={self.ele1}, ..., ele5={self.ele5:.2f})'
    
    dll = CDLL('./test')
    dll.get_result.argtypes = c_void_p, c_int, c_int, c_char_p
    dll.get_result.restype = POINTER(mycontain)
    
    res = dll.get_result(None, 300, 300, None)
    print(res[:5])  # slice to appropriate size to get list of elements
    

    输出:

    [mycontain(id=0, ele1=0, ..., ele5=0.00), mycontain(id=1, ele1=2, ..., ele5=1.01), mycontain(id=2, ele1=4, ..., ele5=2.02), mycontain(id=3, ele1=6, ..., ele5=3.03), mycontain(id=4, ele1=8, ..., ele5=4.04)]
    

    【讨论】:

    • 嘿@mark ..非常感谢您的解决方案...这很有效...我被困了这么多天。你救了我一天。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多