C++ C++;。不同平台上的结构填充/对齐以及布局兼容性的自动检查

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

我已将嵌入式设备连接到PC 以及一些具有许多自定义定义类型FixedPoint\t的字段和数组的大型结构。 FixedPoint_t是一个模板化的POD类,正好有一个数据成员,其大小根据模板参数从char到long不等。无论如何,它会传递
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 },
        ...
    };