使用numpy.frombuffer读取cffi.buffer时如何处理C结构中的成员填充?

时间:2018-01-24 13:31:46

标签: python c numpy struct python-cffi

我必须读取从dll返回的C结构数组并将其转换为Numpy数组。该代码使用Python的cffi模块。

代码到目前为止工作但我不知道如何处理np.frombuffer抱怨的结构中的成员填充:

  

ValueError:缓冲区大小必须是元素大小的倍数

这是我的代码:

from cffi import FFI
import numpy as np

s = '''
    typedef struct
    {
        int a;
        int b;
        float c;
        double d;
    } mystruct;
    '''

ffi = FFI()
ffi.cdef(s)

res = []

#create array and fill with dummy data
for k in range(2):

    m = ffi.new("mystruct *")

    m.a = k
    m.b = k + 1
    m.c = k + 2.0
    m.d = k + 3.0

res.append(m[0])

m_arr = ffi.new("mystruct[]", res)

print(m_arr)

# dtype for structured array in Numpy
dt = [('a', 'i4'),
      ('b', 'i4'),
      ('c', 'f4'),
      ('d', 'f8')]

# member size, 20 bytes
print('size, manually', 4 + 4 + 4 + 8)

# total size of struct, 24 bytes
print('sizeof', ffi.sizeof(m_arr[0]))

#reason is member padding in structs

buf = ffi.buffer(m_arr)
print(buf)

x = np.frombuffer(buf, dtype=dt)
print(x)

如何以干净的方式处理这个问题?

编辑:

如果我在dtype中添加一个额外的数字,似乎可以发生填充:

dt = [('a', 'i4'),
      ('b', 'i4'),
      ('c', 'f4'),
      ('pad', 'f4'),
      ('d', 'f8')]

为什么填充发生在那里? (Win7,64位,Python 3.4 64位)。

但这不是最好的方法。真正的代码更加复杂和动态,因此应该可以以某种方式处理它,对吧?

2 个答案:

答案 0 :(得分:0)

除了注释中给出的其他答案之外,您可以强制cffi打包其结构(即不插入任何填充,类似于使用特定C编译器扩展可以执行的操作):

ffi.cdef("typedef struct { char a; int b; } foo_t;", packed=True)

答案 1 :(得分:0)

最方便的方法是在numpy dtype constructor中使用关键字align=True。这将自动填充。

dt = [('a', 'i4'),
      ('b', 'i4'),
      ('c', 'f4'),
      ('d', 'f8')]

dt_obj = np.dtype(dt, align=True)
x = np.frombuffer(buf, dtype=dt)

(另请参阅结构化数组的Numpy doc