rapidjson with gzstream lib last char' -1'

时间:2014-11-22 11:37:55

标签: c++ ios xcode zlib rapidjson

我编写了简单的gzstream 1.5包装器,用于使用rapidjson 0.1(ios,xcode 6.1)。

问题:我必须在Peek()和Take()中检查eof。否则,我得到'\ 377'( - 1)作为最后一个字符。我知道它在eof上由std :: basic_stream :: get()返回。

什么是更优雅,适当和干净的解决方案?

class GzOutStream {
public:
    GzOutStream(std::string filename) : gs_(new ogzstream(filename.c_str())) {}
    bool Good() { return gs_->good(); }
    void Close() { delete gs_; gs_ = nullptr; }
    size_t Tell() { return gs_->tellp(); }
    void Put(char c) { gs_->put(c); }

    // Not implemented
    char* PutBegin() { return 0; }
    size_t PutEnd(char*) { return 0; }

private:
    ogzstream* gs_;
};

class GzInStream {
public:
    GzInStream(std::string filename) : gs_(new igzstream(filename.c_str())) {}
    bool Good() { return gs_->good(); }
    void Close() { delete gs_; gs_ = nullptr; }
    char Peek() { return gs_->eof()? '\0' : gs_->peek(); }
    char Take() { return gs_->eof()? '\0' : gs_->get(); }
    size_t Tell() { return gs_->tellg(); }
    void Put(char c) { } // Stab

    // Not implemented
    char* PutBegin() { return 0; }
    size_t PutEnd(char*) { return 0; }

private:
    igzstream* gs_;
};

1 个答案:

答案 0 :(得分:1)

以下答案用于对手头问题的一般性讨论。那时我没有查看rapidjson的行为。

您的类旨在成为gzip输入流和rapidjson之间的粘合逻辑,因此您来实现rapidjson期望的接口。它甚至没有良好的功能。 rapidjson期望的界面在EOF上返回'\ 0',所以这是你必须做的唯一选择。如果您使用的gzip流类正在实现C ++流模型,则可以使用“示例istream包装器”一节中https://github.com/miloyip/rapidjson/blob/master/doc/stream.md中描述的模式,该模式以通常与C ++ iostream一起使用的方式执行EOF检测。如果您当前的方式适用于gz流,您也可以保持原样。


只要您没有尝试去过去 eof,您实际上就会遇到输入流保持良好状态的问题。 GzInStream的接口不提供用户在Peek或Take返回无效值之前检测EOF是否被击中的任何可能性。这是由于C ++ iostream的设计:大多数情况下,低级API不指示“流结束”,除非您尝试通过它,因此高级API不提供此功能,因为它非常重要在许多(非文件)案例中实施。

标准C ++ iostream的peek()和get()函数返回int而不是char有一个原因:它们被指定为将从流中读取的字节作为正数量返回(0。 .255在具有8位字节的系统上),同时在出错时返回eof(-1)。您的Peek和Get函数无法返回256个不同的字节和EOF作为不​​同的返回值,因为char无法表示257种可能性。因此,在获取Peek或Take中的角色后,界面的客户必须询问“Good()”,以确定是否确实存在角色。如果您的界面的客户端这样做,无论您返回'\ 377'还是'\ 0'或任何其他值都无关紧要,因为无论如何都会忽略该值。使用“额外”字节的客户端(在我的意见中)是错误的,除非它被设计为忽略您返回的虚假NUL字节。

您可以通过不同方式解决此问题

  • 如上所示修复您的客户,并记录该类的行为。
  • Good Good()返回gs_->good() && !gs_->eof(),在重读eof之前依赖gs_->eof()为真
  • 从Peek和Take返回一个int,就像标准的iostream一样。
  • 从Peek和Take返回boost :: optional,如果遇到eof则返回boost :: none
  • 在EOF的情况下抛出异常形式Peek and Take。

大多数人会立即拒绝最后提出的修复,因为它违反了“不应该用于流量控制的例外”规则。我同意强制客户端使用异常处理来处理EOF是非常不好的风格,但这实际上是唯一不需要更改Peek和Take的签名也不会改变其他函数语义的可能性。我希望第二个建议(改变好)是你用例的方法。