使用 ctypes 的联合操作不起作用

Posted

技术标签:

【中文标题】使用 ctypes 的联合操作不起作用【英文标题】:Union operation with ctypes doesn't work 【发布时间】:2012-06-19 22:30:25 【问题描述】:

我有一个这样的 C 结构:

typedef struct 
    my_domain_type_t type; 
    my_domain_union_t u;
    my_domain_int32_list_t list;
 my_domain_value_t;


typedef struct 
    int32_t min;
    int32_t max;
 my_domain_int32_range_t;

我想从 ctypes 调用的 C 函数:

int64_t myData::get_min(const my_domain_value_t &value)

    int min_value = 0;
    my_domain_type_t dt = value.type;

    if (dt == 0)
    
        my_domain_int32_range_t range = value.u.range;
        min_value = range.min;
        printf("min_value=%d\n", min_value);
    

    return min_value;

ctypes 定义:

class myDomainInt32RangeT(Structure):
    _fields_ = [ ('min', c_long),
                 ('max', c_long) ]

class myDomainUnionT(Union):
    _fields_ = [ ('range', myDomainInt32RangeT ) ]


class myDomainValueT(Structure):
    _fields_ = [ ('type', c_int ),
                 ('u', myDomainUnionT ),
                 ('list', myDomainInt32ListT ) ]

class myData(object):
    def __init__(self):
        self.object = myDataX.myData_new()

    def get_min(self, arg1):
        myDataX.myData_get_min.argtypes = [ c_void_p, POINTER(myDomainValueT) ]
        myDataX.myData_get_min.restype = c_longlong
        return myDataX.mydata_get_min(self.object, arg1)

Python 代码:

mydataY = myData()
domainRange = myDomainInt32RangeT()
domainRange.min = c_long(3)
domainRange.max = c_long(5)
domainUnion = myDomainUnionT()
domainUnion.range = domainRange
domainValue = myDomainValueT()
domainValue.type = 0
domainValue.u = domainUnion
domainValue.list = myDomainInt32ListT()
b = mydataY.get_min( byref(domainValue) )
print(b)

我期望 min_value 的值为 3,但我一直得到 0。C 代码也打印 0。看起来联合没有正确设置/传输。

我做错了什么?

TIA,

约翰

【问题讨论】:

c_long 在一个中,int32_t 在另一个中 - 在您的平台上它们是相同的类型吗? 您是否尝试过在调用 ctypes 函数之前和之后从 Python 打印 domainValue.u.min 以确保它实际上是 3? @sarnold:很好。在 64 位平台上,他很可能以 u.min 和 u.max 作为其预期最小值的低 32 位和高 32 位,因此他调用 min(3, 5) 而不是 min(3, 5) ) 或 (如果是大端) min(0, 3)。我会把它作为答案发布——即使这实际上不是他的问题,这是 一个 问题,并且可能是其他人来这里的最可能原因…… @sarnold:有没有办法检查它?如果不是正确的方法是什么? @abarnett: domainValue.range.u 确实打印了 3。 【参考方案1】:

如果您希望 myDomainInt32RangeT 结构与 my_domain_int32_range_t 结构互换使用,则它们必须定义兼容的类型。但他们没有:

typedef struct 
    int32_t min;
    int32_t max;
 my_domain_int32_range_t;

这定义了一对 int32_t 值。

class myDomainInt32RangeT(Structure):
    _fields_ = [ ('min', c_long),
                 ('max', c_long) ]

这定义了一对任意长的值。

问题在于 int32_t 和 long 不是同一类型。修复很简单:更改一个以匹配另一个(例如,使用 c_int32 而不是 c_long)。

如果你想了解为什么会得到 0,那就有点牵强了。

int32_t 的规则说它必须是 32 位。长期的规则说它必须至少是 32 位。在大多数 32 位平台和 64 位 Windows 上,它正好是 32 位。然而,在大多数其他 64 位平台上,它是 64 位的。 (有关详细信息,请参阅http://en.wikipedia.org/wiki/64-bit 关于 LLP64 与 LP64 的讨论。)

您可能在 64 位 Intel Mac 或 Linux 系统上,并使用默认 Python。因此,您的 long 以及 ctypes.c_long 是 64 位整数。所以,看看 myDomainInt32RangeT 的布局:

1st 32 bits: low half of the 64-bit "min" value
2nd 32 bits: high half of the 64-bit "min" value
3rd 32 bits: low half of the 64-bit "max" value
4th 32 bits: high half of the 64-bit "max" value

相比之下,my_domain_int32_range_t 的布局是这样的:

1st 32 bits: 32-bit "min" value
2nd 32 bits: 32-bit "max" value

所以,如果你构造一个 myDomainInt32RangeT(3, 5),你正在创建的是:

1st 32 bits: 3 (low half of 64-bit 3)
2nd 32 bits: 0 (high half of 64-bit 3)
3rd 32 bits: 5 (low half of 64-bit 5)
4th 32 bits: 0 (high half of 64-bit 5)

当您尝试将其解释为 my_domain_int32_range_t 时,它会看到:

1st 32 bits: 3
2nd 32 bits: 0

所以您的“最小值”值为 3,而您的“最大值”值为 0。

您也可能会通过传递一些代码认为是 128 位而其他代码认为是 64 位的东西来最终分割对象和/或覆盖内存。例如,如果您创建一个 my_domain_int32_range_t,通过引用将其传递给 Python,然后尝试设置其“最大值”值,那么您正在设置一个只有 2 个对象的第 3 位和第 4 位 32 位,这意味着您重新实际覆盖内存中的下一个对象。

以上细节假设您使用的是小端系统(如 x86_64),而不是大端系统或其他系统(是否有任何 VAX 端 LP64 平台?使用 Python?)。在具有 Python 的 64 位 big-endian PowerPC 构建的 PowerMac G5 上,您将得到 (0, 3) 而不是 (3, 0)。但基本思想是一样的。

【讨论】:

它有效。我不清楚这一点:“现在,尝试将 128 位结构解释为一对 32 位整数,当然你会得到 (3, 0)。”你能解释更多吗?值 5 去哪儿了?谢谢!。 嗯,第一个 32 位 int 是 3。第二个 32 位 int 是 0。第三个 32 位 int 是 5,但是你只有两个 32 位 int 变量,所以你没有什么可看的。我会尝试编辑答案以使其更清晰。 这仍然是 C 程序员在转换指针或使用联合时必须处理的问题。不用说,它很少出现在 Python 中,但是当你使用 ctypes 来传递联合时……无论如何,如果还有什么不清楚的地方,请告诉我。如果其他人有很好的链接参考,请添加。

以上是关于使用 ctypes 的联合操作不起作用的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery - 联合上的相关子查询不起作用

带有 vue 2.16.12 的 webpack 模块联合和 single-spa-vue 不起作用

在 knexjs mysql 中取联合多列后总和不起作用

Python数字虚拟按键不起作用

super() 在类方法中不起作用

连接表SQL PHP中列的总和不起作用[重复]