在C++中将一个结构体写入stringstream

3

我有一些结构体如下:

struct dHeader
{
    uint8_t    blockID;
    uint32_t   blockLen;
    uint32_t   bodyNum;
};
struct dBody
{
    char       namestr[10];
    uint8_t    blk_version;
    uint32_t   reserved1;
}

and I have a stringstream as:

std::stringstream Buffer(std::iostream::in | std::iostream::out);

我想将一个 dHdr 和多个 dBody 结构体写入缓冲区中。

Buffer << Hdr1;
Buffer << Body1;
Buffer << Body1;

我遇到了以下错误:

错误:在“Buffer << Hdr1”中没有匹配的运算符“<<”

如果我尝试如下操作:

Buffer.write(reinterpret_cast<char*>(&Hdr1), sizeof(dbHdr1));
Buffer.write(reinterpret_cast<char*>(&Body1), sizeof(Body1));
Buffer.write(reinterpret_cast<char*>(&Body2), sizeof(Body2));

我对打包和内存对齐感到困惑。

  • 将结构体写入stringstream的最佳方法是什么?
  • 如何将stringstream读取为常规字符串?

2
阅读有关“序列化”的内容,您想对结构进行序列化。此外,还有一个boost库可用于此http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html(以及其他)。 - user2485710
有几件事情需要考虑:您希望得到人类可读的东西,还是只需要机器可读的(后者往往更简洁且打包/解析更快)?对于二进制流,您是否需要在流中添加一些内容,以让解包方知道已经打包了什么,还是内容是可预测的? - Tony Delroy
生成的字符串将被下载到卡上,因此不需要人类可读。 - mustafa
1
这里不能重载运算符'<<'吗? - Abhishek Bansal
@mustafa,我将在下面编辑我的答案(比在评论中写更容易)。 - utnapistim
显示剩余2条评论
2个回答

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;
}

(其他结构同理)。

编辑:一个未缓冲的读/写实现具有以下缺点:

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

  • 它是脆弱的;实现仅依赖于结构只包含POD类型。如果稍后向您的结构添加char*,则您的代码将以相同的方式编译,但会暴露未定义的行为。

  • 它是模糊的(您代码的客户端可能期望要么看到定义了I/O的接口,要么假定您的结构不支持序列化)。通常,没有人会想“也许我可以序列化,但使用未缓冲的I/O”-至少在作为自定义struct或class实现的客户端时不会这样想。

通过添加基于未缓冲读写实现的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;
}

这将代码集中在一个接口后面(即,如果您的类不再支持非缓冲写入,则必须在一个地方更改代码),并使您的意图明显(为结构实现序列化)。它仍然很脆弱,但不那么脆弱。


是的。您可以编写一个从结构体转换而来的char,并读取从结构体转换而来的char - utnapistim
可以请您将翻译的文本添加到您的回答中吗? - mustafa
@mustafa,我已经编辑了代码,还包含了一个未缓冲的读取实现,用于输入运算符。 - utnapistim

1
你可以提供一个类似以下的重载 std::ostream::operator<< 的函数:
std::ostream& operator<<(std::ostream&, const dHeader&);
std::ostream& operator<<(std::ostream&, const dBody&);

欲了解更多信息,请参见此stackoverflow问题


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接