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

时间:2012-06-19 22:30:26

标签: python ctypes

我有一个像这样的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,

约翰

1 个答案:

答案 0 :(得分:2)

如果希望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

所以你的“min”值是3,你的“max”值是0。

你也可能最终通过传递一些代码认为是128位的东西来切片对象和/或覆盖内存,而其他代码认为它是64位。例如,如果你创建一个my_domain_int32_range_t,通过引用将它传递给Python,然后尝试设置它的“max”值,你设置一个对象的第3和第4位只有2个,这意味着你'实际上覆盖了内存中的下一个对象。

上面的细节假设您使用的是小端系统(如x86_64),而不是大端系统或其他不同的东西(有没有任何VAX-endian LP64平台?使用Python?)。在具有64位big-endian PowerPC版本的Python的PowerMac G5上,你将获得(0,3)而不是(3,0)。但基本思路是一样的。