0

我想创建一个自定义输入文件流,它会自动去除评论和其他垃圾数据。我想出了以下解决方案:

class FileReader : public std::ifstream
{
public:
  explicit FileReader(const char* fName) { open(fName); }

  ~FileReader() { if (is_open()) close(); }

  template <typename T, bool IsBaseOfSerializable>
  struct DoRead
  {
    void operator()(std::ifstream& ifs, T& data) { ifs >> data; }
  };

  template <typename T>
  struct DoRead<T, true>
  {
    void operator()(FileReader& reader, T& data) { data.Deserialize(reader); }
  };

  template <typename T>
  friend FileReader& operator>>(FileReader& reader, T& data)
  {
    reader.SkipCommentsAndGarbage();
    DoRead<T, std::is_base_of<ISerializable, T>::value> doread;
    doread(reader, data);
    return reader;
  }

  void SkipCommentsAndGarbage() {... }
};

我也有ISerializable包含Serialize/Deserialize方法的接口。我觉得一切都很好。

但我读过我不应该继承std::ifstream并应该创建 custom std::streambuf

您能否解释一下为什么继承不好std::ifstream以及如何创建std::streambuf以类似方式忽略评论和其他数据的自定义?

4

3 回答 3

4

我不清楚你希望你的班级如何工作。这些 operator>>函数在. _std::istream _ std::istream&在某些情况下可以使用转发的想法,但在这种情况下,您不会继承自 std::ifstream; 你包含一个指向 an 的指针istream,并转发给它。(通过不继承,您可以确保您不能以 . 结尾istream&。这是限制性的,但在某些情况下是可以接受的。)

这样做的正常方法是提供一个过滤流缓冲区,它过滤输入文本。例如,如果注释是从 a#到行尾,而您不必担心引号等问题,那么以下简单的方法将起作用:

class UncommentStreambuf : public std::streambuf
{
    std::streambuf* mySource;
    std::istream*   myOwner;
    char            myBuffer;
protected:
    int underflow() override
    {
        int results = mySource->sbumpc();
        if ( results == '#' ) {
            while ( mySource->sgetc() != '\n' ) {
                mySource->sbumpc();
            }
        }
        if (results != traits_type::eof()) {
            myBuffer = results;
            setg( &myBuffer, &myBuffer, &myBuffer + 1 );
        } else {
            setg( nullptr, nullptr, nullptr );
        }
        return results;
    }
public:
    UncommentStreambuf( std::streambuf* source )
        : mySource( source )
        , myOwner( nullptr )
    {
    }
    UncommentStreambuf( std::istream& source )
        : mySource( source.rdbuf() )
        , myOwner( &source )
    {
        source.rdbuf( this );
    }
    ~UncommentStreambuf()
    {
        if ( myOwner != nullptr ) {
            myOwner->rdbuf( mySource );
        }
    }
};

如果您需要处理其他类型的评论,或者担心引用的评论字符,您将需要更多的逻辑(可能有一个私有缓冲区来收集字符,以便测试多个字符的序列)。

于 2014-04-24T13:27:59.223 回答
3

Because publicly inheriting to modify the behavior of ifstream will lead to unintended consequences, because it still is an istream and will be treated as such in many cases - stripping away your custom behavior:

  • When passing FileReader to a function that expects an ::std::istream&, your custom functionality will not be used

  • Any class C with an operator like template<typename T> T& operator>>(T& in, C& target); will be unusable

  • Whenever any functionality of the underlying istream is used directly, your wrapper functions do not work

In your case, you could simply switch to having your ifstream as a member or change its inheritance to private.

于 2014-04-24T13:28:27.597 回答
1

它基本上是std::iostream自定义的设计std::streambuf,所有上下文特定的功能都在这里。

于 2014-04-24T13:26:18.977 回答