C++ 如何在C+中序列化结构数据+;?

C++ 如何在C+中序列化结构数据+;?,c++,serialization,C++,Serialization,在一次采访中,我被要求对数据进行序列化(这样数据就可以存储在缓冲区中并通过网络发送)。这就是我想到的- struct AMG_ANGLES { float yaw; float pitch; float roll; }; char b[sizeof(struct AMG_ANGLES)]; char* encode(struct AMG_ANGLES *a) { std::memcpy(b, &a, sizeof(struct AMG_ANGLES)

在一次采访中,我被要求对数据进行序列化(这样数据就可以存储在缓冲区中并通过网络发送)。这就是我想到的-

struct AMG_ANGLES {
    float yaw;
    float pitch;
    float roll;
};

char b[sizeof(struct AMG_ANGLES)];

char* encode(struct AMG_ANGLES *a)
{

    std::memcpy(b, &a, sizeof(struct AMG_ANGLES));
    return b;
}

void decode(char* data)
{
 // check endianess   
    AMG_ANGLES *tmp; //Re-make the struct
    std::memcpy(&tmp, data, sizeof(tmp));
}
这是正确的吗?有人能提供替代设计吗?我没有收到回电,所以我只是想知道我本可以改进的地方

这是正确的吗

很可能是没有

序列化的要点是将数据转换为完全独立于平台的形式-例如,不依赖于endianess之类的东西,或者如果
float
是IEEE 754或非常不同的东西。这需要:

a) 在预期格式上严格一致-例如,如果它是某种文本(XML、JSON、CSV等),或者如果它是“原始二进制”,每个字节的含义都有明确的定义(例如,可能“字节1始终是有效位的最低8位”)

b) 正确转换为任何预期格式(例如,可能类似于确保字节1始终是有效位的最低8位,而不考虑任何/所有平台差异)


但是,;至少在技术上,代码不应该是可移植的,并且规范(“预期格式协议”)恰好与您最终为代码设计的唯一平台所获得的内容相匹配;因此,至少从技术上讲,代码是正确的。

最好创建一些类,如std::stringstream。。 std::stringstream不适合保存二进制数据,但它的工作方式与您想要的相同。 因此,我可以制作一些使用std::stringstream的示例

此代码仅实现序列化,但也添加了反序列化代码

// C++11
template < typename T, typename decltype(std::declval<T>().to_string())* = nullptr>
    std::ostream& operator<< (std::ostream& stream, T&& val)
{
    auto str = val.to_string();
    std::operator <<(stream, str);
    return stream;
}

struct AMG_ANGLES {
    float yaw;
    float pitch;
    float roll;
    std::string to_string() const
    {
        std::stringstream stream;
        stream << yaw << pitch << roll;
        return stream.str();
    }
};

void Test()
{
    std::stringstream stream;
    stream << 3 << "Hello world" << AMG_ANGLES{1.f, 2.f, 3.f };
}

/C++11
模板

std::ostream&operator可能会有很多改进,但我建议您检查一下,而不是告诉所有的改进。它是广泛使用的序列化/反序列化库,因此考虑了许多关键点

我的一些想法是:

  • 您的代码取决于由于和而运行程序的硬件。因此,序列化数据不可移植,并且依赖于编译器

  • char*encode(struct AMG_ANGLES*a)
    函数返回
    char*
    ,它可能泄漏。为了防止这个问题,让
    std::unique_ptr
    决定它的生存期,或者用类包装它。但是要想办法摆脱指针

  • 模板化序列化/反序列化操作。否则,您可以为其他类型编写相同的函数

    template<typename T>
    char* encode( T* a ) // I leave signature as is, just to demonstrate
    {
         std::memcpy( b , &a , sizeof(T) );
         return b;
    }
    
    模板
    char*encode(T*a)//我保留签名原样,只是为了演示一下
    {
    std::memcpy(b,&a,sizeof(T));
    返回b;
    }
    
  • 如果格式由您决定,最好选择可读的格式,而不是二进制存档,如
    JSON
    XML
  • 有人能用C语言给出替代设计吗

    “标准”方法是使用
    printf
    scanf
    创建数据的ascii表示:

    #include <limits.h>
    #include <math.h>
    #include <stdio.h>
    #include <assert.h>
    #include <float.h>
    
    struct AMG_ANGLES {
        float yaw;
        float pitch;
        float roll;
    };
    
    // declare a buffer at least this long to be sure encode works properly
    #define AMG_ANGLES_BUFSIZE  ( \
        3 * ( /* 3 floats */ \
             2 + /* digit and dot */ \
             FLT_DECIMAL_DIG - 1 + /* digits after dot */ \
             4 /* the 'e±dd' part */ \
        ) \
        + 2 /* spaces */ \
        + 1 /* zero terminating character */ \
    )
    
    int encode(char *dest, size_t destsize, const struct AMG_ANGLES *a) {
        return snprintf(dest, destsize, "%.*e %.*e %.*e", 
             FLT_DECIMAL_DIG - 1, a->yaw, 
             FLT_DECIMAL_DIG - 1, a->pitch, 
             FLT_DECIMAL_DIG - 1, a->roll);
        // my pedantic self wants to add `assert(snprintf_ret < AMG_ANGLES_BUFSIZE);`
    }
    
    int decode(struct AMG_ANGLES *dest, const char *data) {
        return sscanf(data, "%e %e %e", &dest->yaw, &dest->pitch, &dest->roll) == 3 ? 0 : -1;
    }
    
    int main() {
       char buf[AMG_ANGLES_BUFSIZE];
       const struct AMG_ANGLES a = { FLT_MIN, FLT_MAX, FLT_MIN };
       encode(buf, sizeof(buf), &a);
       struct AMG_ANGLES b;
       const int decoderet = decode(&b, buf);
       assert(decoderet == 0);
       assert(b.yaw == FLT_MIN);
       assert(b.pitch == FLT_MAX);
       assert(b.roll == FLT_MIN);
    }
    
    或删除代码重复:

    int decode2(struct AMG_ANGLES *dest, const char *data) {
        // array of pointers to floats to fill
        float * const dests[] = { &dest->yaw, &dest->pitch, &dest->roll };
        const size_t dests_cnt = sizeof(dests)/sizeof(*dests);
        errno = 0;
        for (int i = 0; i < dests_cnt; ++i) {
            char *endptr = NULL;
            *dests[i] = strtof(data, &endptr);
            if (errno != 0 || endptr == data) return -1;
            // space separates numbers, last number is followed by zero
            const char should_be_char = i != dests_cnt - 1 ? ' ' : '\0';
            if (*endptr != should_be_char) return -1;
            data = endptr + 1;
        }
        return 0;
    }
    
    int decode2(结构AMG_角度*dest,常量字符*data){
    //指向要填充的浮点数的指针数组
    float*const dests[]={&dest->偏航,&dest->俯仰,&dest->横摇};
    const size\u t dests\u cnt=sizeof(dests)/sizeof(*dests);
    errno=0;
    对于(int i=0;i

    我需要使用一些google和重读chux的答案来正确地回忆如何在
    printf
    中使用
    FLT\u DECIMAL\u DIG
    来打印浮点,这很可能是因为我很少使用浮点。

    std::memcpy()
    在C代码中?我的意思是像一般的嵌入式C/C++代码。现在编辑这个问题。@DavidC.Rankin-是否
    htonl()
    管理
    float
    数据的转换?鉴于
    tmp
    是指针,
    std::memcpy(&tmp,data,sizeof(tmp))是错误的;你需要<代码> siZeof(*TMP)< /C>。一旦有C++的答案,请不要删除C++语言标签。您的编辑现在使它们变得无关紧要。我将回滚更改。如果你只对C感兴趣,那么请问一个新问题。
    
    int decode2(struct AMG_ANGLES *dest, const char *data) {
        // array of pointers to floats to fill
        float * const dests[] = { &dest->yaw, &dest->pitch, &dest->roll };
        const size_t dests_cnt = sizeof(dests)/sizeof(*dests);
        errno = 0;
        for (int i = 0; i < dests_cnt; ++i) {
            char *endptr = NULL;
            *dests[i] = strtof(data, &endptr);
            if (errno != 0 || endptr == data) return -1;
            // space separates numbers, last number is followed by zero
            const char should_be_char = i != dests_cnt - 1 ? ' ' : '\0';
            if (*endptr != should_be_char) return -1;
            data = endptr + 1;
        }
        return 0;
    }