【发布时间】:2020-12-30 08:44:40
【问题描述】:
我正在编写一个 Python 应用程序,它将编写一个二进制文件。该文件将由在嵌入式目标上运行的一些 C 代码解析。
我相信我可以通过从 Struct class 派生来做到这一点,但是打包格式很糟糕,而且我的所有结构都是 little-endian,所以我想到了使用 ctypes 包。
假设我有以下 C 结构:
struct my_c_struct
{
uint32_t a;
uint16_t b;
uint16_t table[];
};
在 C 端,我使用转换为内存缓冲区的指针对该结构进行操作,所以我可以这样做:
uint8_t buf[128];
struct my_c_struct *p = (struct my_c_struct*) buf;
p->table[0] = 0xBEEF;
如何在 Python 中最好地表示这一点?我的第一个尝试是:
class MyCStruct(ctypes.LittleEndianStructure):
c_uint32 = ctypes.c_uint32
c_uint16 = ctypes.c_uint16
_pack_ = 1
_fields_ = [
("a", c_uint32),
("b", c_uint16),
]
def __init__(self, a, b):
"""
Constructor
"""
super(ctypes.LittleEndianStructure, self).__init__(a, b)
self.table = []
def pack(self):
data = bytearray(self.table)
return bytearray(self)+data
pack() 方法背后的想法是,它将在结构的末尾组装可变长度表。请注意,我不知道 table 在创建对象时有多少条目。
我实现它的方式显然行不通。所以我正在考虑将 ctypes-devived 类嵌套在纯 Python 类中:
class MyCStruct:
class my_c_struct(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [ ("a", ctypes.c_uint32),
("b", ctypes.c_uint16) ]
def __init__(self, a, b):
"""
Constructor
"""
self.c_struct = self.my_c_struct(a,b)
self.table = []
def pack(self):
self.c_struct.b = len(self.table)
x = bytearray(self.c_struct)
y = bytearray()
for v in self._crc_table:
y += struct.pack("<H", v)
return x + y
这是一个好方法吗?我不想深入兔子洞,只是为了发现有更好的方法。
警告:我正在使用 Python 2(请不要问...),因此仅 Python 3 的解决方案对我没有用,但对宇宙的其他部分有用。
干杯!
【问题讨论】:
-
不确定这是否有帮助,但您是否查看过 Python 中的 struct 标准库(适用于 2.7)?查看底部的“类”部分。
-
感谢您的回答!是的,我做到了,但是格式化语法有点糟糕,我试图尽可能地像 Python 一样。
-
你看过Cap'n Proto吗?它是协议缓冲区的一种变体,旨在减少 CPU 密集度以进行序列化/反序列化。
-
“在 C 端,我使用转换为内存缓冲区的指针对该结构进行操作”——从而获得未定义的行为。你可能会侥幸逃脱,但随着编译器在优化方面变得更聪明、更积极,诸如此类的严格混叠违规似乎越来越频繁地引发不需要的行为。
-
@JohnBollinger pfft,只是
alias gcc="gcc -fno-strict-aliasing":')
标签: python c ctypes python-2.x