具有C struct arrary的Python ctypes定义

时间:2018-07-27 00:55:31

标签: python ctypes

如何将如下所示的C结构表示为python ctypes并对其进行访问?它具有结构数组作为元素,并带有另一个结构。

struct hello {
               __s64 f1;
               __u64 f2;
               __u64 f3;
               __s32 f4;
               __u32 f5;
           };

struct world {
               __u64 f1;
               __u64 f2;
               __u16 f3;
               __u16 f4;
               __u32 f5;
               struct hello h[0];
           };

我尝试了以下操作,但没有用:

from ctypes import *

class hello(Structure):
    _pack_ = 1
    _fields_ = [ ("f1", c_longlong),
                 ("f2", c_ulonglong),
                 ("f3", c_ulonglong),
                 ("f4", c_long),
                 ("f5", c_ulong)
              ]
class world(Structure):
    _pack_ = 1
    _fields_ = [ ("f1", c_ulonglong),
                 ("f2", c_ulonglong),
                 ("f3", c_uint16),
                 ("f4", c_uint16),
                 ("f5", c_uint32),
                 ("f6", hello)  #Is this correct? 
              ]

1 个答案:

答案 0 :(得分:2)

您所做的不正确。为了使world的最后一个成员是N个hello的数组,它的类型必须为hello * N,而不是hello

但是在这里,N为0,所以这实际上不起作用。

在C中,这称为“结构黑客”或“结构数组黑客”。 world结构不是可以按原样构建并传递的真实结构。相反,您要做的是m分配sizeof(world) + N * sizeof(hello)个字节,将其强制转换为world *,然后将该指针传递给周围。由于数组索引不会在C语言中进行任何类型检查,因此my_world->h[2]就可以正常工作(假设您在hello的末尾分配了至少3个world s的足够内存)。

但是它不能在Python中工作。

嗯,它可以多种,但是您必须做更多的工作。


第一种选择是不实际全局定义world,而是在每次要处理Structure时定义一个新的world类。

例如。假设我们调用了一些函数来告诉我们有多少个问候:

n = lib.number_of_hellos()

现在我们要调用一个函数,该函数为我们提供了包含许多问候的世界。所以:

class world(Structure):
    _pack_ = 1
    _fields_ = [ ("f1", c_ulonglong),
                 ("f2", c_ulonglong),
                 ("f3", c_uint16),
                 ("f4", c_uint16),
                 ("f5", c_uint32),
                 ("f6", hello * n)
              ]

这时,您可以更改restype中的lib.get_world以返回POINTER(world),也可以强制转换返回的结果。


或者,您可以将world定义为始终具有1个hello,但是,您可以构造一个{{1而不是直接从hello指针访问world }}指向hello * N地址的指针(或仅指向hello的地址加上类型的world的偏移量;两种方法都相同)。


如果您需要创建一个包含N个hello的世界才能传递给C怎么办?

您可以再次使用第一个技巧,构造一个新的hello类,该类以N个world的数组结尾,然后创建一个实例以将其传递给C。

或者您可以使用与C代码相同的技巧:保留全局单{hello hello类,并通过创建缓冲区然后投射该对象来构造world对象到world,或构造一个world对象,然后在其上调用world


您还可以采取其他解决方法来简化生活。例如,如果您是自己编写C代码(并且不能只是不使用struct hack),则可以轻松添加一对由no-resize hello组成的C函数。 ,world的数组以及N-hello hello中的计数,反之亦然。

或者,或者,仅使world类型在末尾具有指向hellos数组的指针,而不是直接包含它,并编写C函数以在它们之间来回转换。

然后,您只需nonhackyworld设置这些函数,并在需要传递或处理ctypes时将它们调用(甚至将它们设置为其他函数的值类型转换器)。

即使您不能添加C函数,也可以在world中编写这些函数。它们将使创建缓冲区和强制转换指针变得非常丑陋,但是您只需编写一次(并调试segfaults),然后在任何地方使用它们。