C++ C++;。不同平台上的结构填充/对齐以及布局兼容性的自动检查
我已将嵌入式设备连接到PC 以及一些具有许多自定义定义类型FixedPoint\t的字段和数组的大型结构。 FixedPoint_t是一个模板化的POD类,正好有一个数据成员,其大小根据模板参数从char到long不等。无论如何,它会传递C++ C++;。不同平台上的结构填充/对齐以及布局兼容性的自动检查,c++,c++11,struct,embedded,padding,C++,C++11,Struct,Embedded,Padding,我已将嵌入式设备连接到PC 以及一些具有许多自定义定义类型FixedPoint\t的字段和数组的大型结构。 FixedPoint_t是一个模板化的POD类,正好有一个数据成员,其大小根据模板参数从char到long不等。无论如何,它会传递static_assert((std::is_pod::value==true),“”) 如果这个大结构在嵌入式系统和控制PC上都有兼容的底层内存表示,这将是一件好事。这将大大简化通信协议,使之能够执行“将偏移量N设置为V的字/字节”等命令。假设两个平台上的en
static_assert((std::is_pod::value==true),“”)代码>
如果这个大结构在嵌入式系统和控制PC上都有兼容的底层内存表示,这将是一件好事。这将大大简化通信协议,使之能够执行“将偏移量N设置为V的字/字节”等命令。假设两个平台上的endianess相同
我在这里看到3种解决方案:
使用类似于#pragma的东西包装在两边。
但是,当我将属性((打包的)放入结构声明时,收到了警告
警告:由于未打包的非POD字段,将忽略打包属性。
这是因为FixedPoint\u t未声明为打包的。
我不想将其声明为打包,因为这种类型在整个程序中广泛使用,打包可能导致性能下降
进行正确的结构序列化。这是不可接受的,因为代码膨胀,额外的RAM使用…协议将更加复杂,因为我需要随机访问结构。现在我认为这不是一个选择
手动控制填充。我可以添加一些字段,对其他字段重新排序…只是为了在两个平台上都不添加填充。这将使我目前感到满意。但我需要一个好方法来编写一个测试,告诉我填充是否存在。
我可以将每个字段的sizeof()和sizeof(struct)进行比较。
我可以比较两种平台上每个结构字段的offsetof()。
两个变种都够丑的了
你推荐什么?我对测试中的手动填充控制和自动填充检测特别感兴趣
编辑:比较两个平台上的sizeof(大结构)是否足以检测布局兼容性(假设endianess相等)??我认为如果填充物不同,尺寸就不应该匹配
EDIT2:
//this struct should have padding on 32bit machine
//and has no padding on 8bit
typedef struct
{
uint8_t f8;
uint32_t f32;
uint8_t arr[5];
} serialize_me_t;
//count of members in struct
#define SERTABLE_LEN 3
//one table entry for each serialize_me_t data member
static const struct {
size_t width;
size_t offset;
// size_t cnt; //why we need cnt?
} ser_des_table[SERTABLE_LEN] =
{
{ sizeof(serialize_me_t::f8), offsetof(serialize_me_t, f8)},
{ sizeof(serialize_me_t::f32), offsetof(serialize_me_t, f32)},
{ sizeof(serialize_me_t::arr), offsetof(serialize_me_t, arr)},
};
void serialize(void* serialize_me_ptr, char* buf)
{
const char* struct_ptr = (const char*)serialize_me_ptr;
for(int i=0; i<SERTABLE_LEN; I++)
{
struct_ptr += ser_des_table[i].offset;
memcpy(buf, struct_ptr, ser_des_table[i].width );
buf += ser_des_table[i].width;
}
}
//此结构在32位计算机上应该有填充
//而且上面没有填充物
类型定义结构
{
uint8_t f8;
uint32_t f32;
uint8_t arr[5];
}连载我;
//结构中的成员计数
#定义SERTABLE_LEN 3
//每个序列化数据成员一个表项
静态常量结构{
尺寸和宽度;
尺寸偏差;
//size\u t cnt;//我们为什么需要cnt?
}服务表[服务表]=
{
{sizeof(serialize\me\t::f8),offsetof(serialize\me\t,f8)},
{sizeof(serialize\me\t::f32),offsetof(serialize\me\t,f32)},
{sizeof(serialize\me\t::arr),offsetof(serialize\me\t,arr)},
};
void serialize(void*serialize\u me\u ptr,char*buf)
{
const char*struct_ptr=(const char*)序列化_me_ptr;
对于(int i=0;i我强烈建议使用选项2:
- 您正在保存以备将来更改(新的PCD/ABI、编译器、平台等)
- 如果考虑得当,代码膨胀可以保持在最低限度。每个方向只需要一个函数
- 您可以(半)自动地(半)创建所需的表/代码(我使用Python),这样双方将保持同步
- 无论如何,您都应该向数据中添加CRC。因为您可能不想在rx/tx中断中计算CRC,所以您必须提供一个数组
- 直接使用结构将很快成为维护的噩梦。如果其他人必须跟踪此代码,则更糟
- 协议等都倾向于重用。如果它是一个具有不同endianess的平台,那么另一种方法会非常有用
要创建数据结构和ser/des表,可以使用offsetof
获取结构中每种类型的偏移量。如果该表是包含文件,则可以在两侧使用。您甚至可以通过Python脚本创建结构和表。将其添加到构建过程中可确保它始终是最新的,并避免添加打字
例如(在C中,只是为了获得想法):
如果不是自动创建的,我会使用宏来生成数据。可能包括两次文件:一次生成结构定义,一次生成表。这可以通过重新定义中间的宏来实现
您应该关心有符号整数和浮点的表示(实现定义,浮点很可能是标准建议的IEEE754)
作为width
字段的替代,您可以使用“类型”代码(例如映射到实现定义类型的char
。这样,您可以添加宽度相同但编码不同的自定义类型(例如uint32_t和IEEE754-float
)。这将从物理机器中完全提取网络协议编码(最佳解决方案)。注意:注意:注意,这会妨碍您使用普通编码,因为普通编码不会使代码复杂化(字面上).对不起,你能提供一个更完整的使用ser_des_表的例子吗?我想你还没有完全了解…我不明白你缺少什么。对于每个接收/发送的数据包,你只需在表中重复(de)将数据包数据从缓冲区序列化为结构。在我的原始问题中添加了代码。我明白你的想法了吗?我只想问一下为什么我们需要表中的cnt?@sigmaN:cnt
,因为第二个字段是数组。对不起,我真的应该也添加它(现在完成,请参见编辑)。如果没有数组,则当然不需要该字段。如果要节省内存,也可以使用小于size\t
的类型,前提是确定值不会太大(最好将width
和cnt
分别具有uint16\t
甚至无符号字符<
// protocol.inc
typedef struct {
uint32_t i;
uint 16_t s[5];
uint32_t j;
} ProtocolType;
static const struct {
size_t width;
size_t offset;
size_t cnt;
} ser_des_table[] = {
{ sizeof(ProtocolType.i), offsetof(ProtocolType.i), 1 },
{ sizeof(ProtocolType.s[0]), offsetof(ProtocolType.s), 5 },
...
};