Writing a struct into a stringstream in C++

mus*_*afa 3 c++ stringstream

I have some structs as:

struct dHeader
{
    uint8_t    blockID;
    uint32_t   blockLen;
    uint32_t   bodyNum;
};
struct dBody
{
    char       namestr[10];
    uint8_t    blk_version;
    uint32_t   reserved1;
}
Run Code Online (Sandbox Code Playgroud)

and I have a stringstream as:

std::stringstream Buffer(std::iostream::in | std::iostream::out);
Run Code Online (Sandbox Code Playgroud)

I want to write a dHdr and multiple dBody structs into Buffer with

Buffer << Hdr1;
Buffer << Body1;
Buffer << Body1;
Run Code Online (Sandbox Code Playgroud)

I get the error:

error: no match for 'operator<<' in 'Buffer << Hdr1'

If I try it with:

Buffer.write(reinterpret_cast<char*>(&Hdr1), sizeof(dbHdr1));
Buffer.write(reinterpret_cast<char*>(&Body1), sizeof(Body1));
Buffer.write(reinterpret_cast<char*>(&Body2), sizeof(Body2));
Run Code Online (Sandbox Code Playgroud)

I get confused about the packing and memory alignment.

  • What is the best way to write a struct into a stringstream?
  • And read the stringstream into a regular string?

utn*_*tim 5

对于您的每个结构,您需要定义类似于以下内容的内容:

struct dHeader
{
    uint8_t    blockID;
    uint32_t   blockLen;
    uint32_t   bodyNum;
};

std::ostream& operator<<(std::ostream& out, const dHeader& h)
{
     return out << h.blockID << " " << h.blockLen << " " << h.bodyNum;
}

std::istream& operator>>(std::istream& in, dHeader& h) // non-const h
{
    dHeader values; // use extra instance, for setting result transactionally
    bool read_ok = (in >> values.blockID >> values.blockLen >> values.bodyNum);

    if(read_ok /* todo: add here any validation of data in values */)
        h = std::move(values);
    /* note: this part is only necessary if you add extra validation above
    else
        in.setstate(std::ios_base::failbit); */
    return in;
}
Run Code Online (Sandbox Code Playgroud)

(其他结构类似)。

编辑:无缓冲读/写实现具有以下缺点:

  • 它是未格式化的;如果您控制编译和运行它的位置,这对于小型实用程序应用程序可能不是问题,但通常情况下,如果您获取序列化数据并在不同架构上运行/编译应用程序,则会遇到字节序问题;您还需要确保您使用的类型不依赖于架构(即继续使用uintXX_t类型)。

  • 它很脆;实现取决于仅包含 POD 类型的结构。如果稍后将 char* 添加到您的结构中,您的代码将编译相同,只是公开未定义的行为。

  • 它是模糊的(您的代码的客户端希望看到为 I/O 定义的接口或假设您的结构不支持序列化)。通常,没有人认为“也许我可以序列化,但使用非缓冲 I/O”——至少在作为自定义结构或类实现的客户端时不会。

这些问题可以通过添加 i/o 流操作符来改善,在无缓冲读取和写入方面实现。

上述运算符的示例代码:

std::ostream& operator<<(std::ostream& out, const dHeader& h)
{
     out.write(reinterpret_cast<char*>(&h), sizeof(dHeader));
     return out;
}

std::istream& operator>>(std::istream& in, dHeader& h) // non-const h
{
    dHeader values; // use extra instance, for setting result transactionally
    bool read_ok = in.read( reinterpret_cast<char*>(&values), sizeof(dHeader) );

    if(read_ok /* todo: add here any validation of data in values */)
        h = std::move(values);
    /* note: this part is only necessary if you add extra validation above
    else
        in.setstate(std::ios_base::failbit); */
    return in;
}
Run Code Online (Sandbox Code Playgroud)

这将代码集中在接口后面(即,如果您的类不再支持无缓冲写入,您将不得不在处更改代码),并使您的意图显而易见(为您的结构实现序列化)。它仍然很脆,但不那么脆。