【问题标题】:Writing a struct into a stringstream in C++在 C++ 中将结构写入字符串流
【发布时间】:2014-05-28 07:30:10
【问题描述】:

我有一些结构:

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

我有一个字符串流:

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

我想用

将一个 dHdr 和多个 dBody 结构写入 Buffer
Buffer << Hdr1;
Buffer << Body1;
Buffer << Body1;

我得到错误:

错误:'缓冲区

如果我尝试一下:

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

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

  • 将结构写入字符串流的最佳方法是什么?
  • 然后阅读 将字符串流转换成常规字符串?

【问题讨论】:

  • 阅读serialization,你想序列化 struct 。还有一个用于boost.org/doc/libs/1_55_0/libs/serialization/doc/index.html(以及其他)的 boost 库
  • 您需要考虑几件事:您想要人类可读的东西,还是机器可读的东西(后者往往更简洁,打包/解析更快)?对于二进制流,您是否需要流中的某些内容让解包方知道已打包的内容,或者内容是可预测的?
  • 生成的字符串将下载到卡上,因此不需要人类可读。
  • 重载运算符 '
  • @mustafa,错误消息(与运算符不匹配...)准确地指出您没有超载operator&lt;&lt;。请参阅下面的答案。

标签: c++ stringstream


【解决方案1】:

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

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”——至少在作为自定义结构或类实现的客户端时不会。

这些问题可以通过添加 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*(从 struct 转换而来)并读取从 struct 转换而来的 char*。
  • 您也可以将其添加到您的答案中吗?
  • @mustafa,我已经编辑了代码以包含输入运算符的无缓冲读取实现。
【解决方案2】:

您可以为std::ostream::operator&lt;&lt; 提供重载

std::ostream& operator<<(std::ostream&, const dHeader&);
std::ostream& operator<<(std::ostream&, const dBody&);

有关更多信息,请参阅stackoverflow question

【讨论】:

    猜你喜欢
    • 2015-07-09
    • 2015-11-29
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多