我需要一个与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并且应用了一些元类魔法,但这种方式与联盟谎言的元类冲突。有什么想法吗?或者我应该坚持我所拥有的东西?
答案 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.Union
和ctypes.Structure
的特殊规则。具体来说,ctypes.Union
或ctypes.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__
方法可能会中断。