Python ctypes位域

时间:2013-12-04 16:18:34

标签: python ctypes

我需要一个与ctypes Structures兼容的位域定义,这样我就可以将它用作指向内存映射硬件寄存器集的指针,即

class RegFile(Structure):
    _fields_ = [
        ('ticks', c_uint32),
        ('id', id_bitfield),
    ]

理想情况下,它们是非常恐怖的东西,并且表现得非常像dicts。我已经设法将大部分血腥细节工作到我现在拥有工厂函数的位置,这使得bitfield类与制作namedtuples类似。这是一个剥离标准浮点字段的例子。

def make_register(name, fields, basetype=c_uint32):
    # Define the underlying bitfield type
    bitfield = type(name + '_bitfield', (LittleEndianStructure, ), {})
    bitfield._pack_ = 1
    bitfield._fields_ = fields

    # Set up the union
    d = {
        '_fields_' : [('base', basetype), ('_b', bitfield)],
        '_anonymous_' : ('_b',),
        '__iter__' : _register_iter,
        'keys' : _register_keys,
        'items' : _register_items,
        'update' : _register_update
    }
    return type(name, (Union, ), d)

ieee754_fields = [
    ('mantissa', c_uint, 23),
    ('exponent', c_uint, 8),
    ('sign', c_uint, 1)
]
IEEE754 = make_register('IEEE754', ieee754_fields, c_float)
x = IEEE754()

这很有效,但不是特别的Pythonic感觉语法。理想情况下,我有一些答案可以让我将位域类定义为:

class IEEE754(Register):
    """Individual bitfields of a standard IEEE-754 floating point number."""
    _fields_ = ieee754_fields
    _basetype_ = c_float

但是我无法让Register类变成一个东西。似乎Register应该继承自Union并且应用了一些元类魔法,但这种方式与联盟谎言的元类冲突。有什么想法吗?或者我应该坚持我所拥有的东西?

1 个答案:

答案 0 :(得分:2)

您可以通过创建扩展type(ctypes.Union)的注册元类来实现您所描述的内容,如下所示:

class Register(type(ctypes.Union)):
    def __new__(mcs, name, bases, dict):
        #make the bitfield class
        class regBitfield(ctypes.LittleEndianStructure):
            _fields_ = dict['_fields_']
        #set up the Union
        class regUnion(ctypes.Union):
            _fields_ = [('base', dict['_basetype_']), ('_b', regBitfield)]
            _anonymous_ = ('_b', )
            __iter__ = _register_iter
            keys = _register_keys
            items = _register_items
            update = _register_update
        #return a subclass of the temporary regUnion class
        return type(ctypes.Union).__new__(mcs, name, (regUnion,), dict)

class IEEE754: #for Python3 use class IEEE754(metaclass=Register):
    __metaclass__ = Register #omit this line Python3
    _fields_ = [('mantissa', ctypes.c_uint, 23),
                ('exponent', ctypes.c_uint, 8),
                ('sign', ctypes.c_uint, 1)
               ]
    _basetype_ = ctypes.c_float

这与您的make_register函数的工作方式几乎相同,但利用了类系统的现有机制以及扩展ctypes.Unionctypes.Structure的特殊规则。具体来说,ctypes.Unionctypes.Structure的子类的任何子类将扩展 _fields_列表,而不是替换它。知道了这一点,我们就可以在最终类扩展的__new__元类的Register内创建临时类。有了以上内容:

>>> myFloat = IEEE754(42.0)
>>> print(myFloat.base)
42.0
>>> print(myFloat.mantissa)
2621440L
>>> fltReg._fields_
[('mantissa', <class 'ctypes.c_ulong'>, 23), ('exponent', <class 'ctypes.c_ulong'>, 8), ('sign', <class 'ctypes.c_ulong'>, 1)]

如果正在创建的类型有多个基数,Register.__new__方法可能会中断。