使用 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 的联合操作不起作用的主要内容,如果未能解决你的问题,请参考以下文章