C ++自定义惰性迭代器

时间:2019-06-09 19:27:00

标签: c++ iterator

我有一个稍微简单的文本文件解析器。我解析的文本分为{ block data }表示的块。

我的解析器有一个string read()函数,该函数可以返回令牌,因此在上面的示例中,第一个令牌是{,后跟block,然后是data,然后是}

为了减少重复,我想编写一个类似于生成器的迭代器,使我可以编写类似于以下JavaScript代码的内容:

* readBlock() {
    this.read(); // {

    let token = this.read();

    while (token !== '}') {
        yield token;

        token = this.read();
    }
}

这又使我可以使用简单的for-of语法:

for (let token of parser.readBlock()) {
    // block
    // data
}

对于C ++,我想要类似的东西:

for (string token : reader.read_block())
{
    // block
    // data
}

我四处搜寻以查看是否可以使用迭代器来完成此操作,但是我无法确定是否可以拥有像这样的没有定义开始或结束的惰性迭代器。也就是说,其开始是阅读器的当前位置(字符向量中的整数偏移量),而其结束是找到标记}时。 我不需要构造任意的迭代器,也不需要反向迭代,也不必查看两个迭代器是否相等,因为纯粹是为了减少线性迭代的重复性。

目前,每次我想读取一个块时,都需要重写以下内容:

stream.skip(); // {
while ((token = stream.read()) != "}")
{
    // block
    // data
}

这变得非常混乱,尤其是当我在块内有块时。为了支持块内的块,迭代器必须全部引用同一读者的偏移量,以使内部块将偏移量提前,而外部块将从该偏移量重新开始迭代(内部完成后)。 / p>

这可以用C ++实现吗?

1 个答案:

答案 0 :(得分:1)

为了在for-range循环中可用,类必须具有返回迭代器的成员函数begin()和end()。

什么是迭代器?满足一组要求的任何对象。有几种迭代器,具体取决于允许哪些操作。我建议实现一个最简单的输入迭代器:https://en.cppreference.com/w/cpp/named_req/InputIterator

class Stream
{
public:
    std::string read() { /**/ }
    bool valid() const { /* return true while more tokens are available */ }
};

class FileParser
{
    std::string current_;
    Stream* stream_;
public:
    class iterator
    {
        FileParser* obj_;
    public:
        using value_type = std::string;
        using reference = const std::string&;
        using pointer = const std::string*;
        using iterator_category = std::input_iterator_tag;
        iterator(FileParser* obj=nullptr): obj_ {obj} {}
        reference operator*() const { return obj_->current_; }
        iterator& operator++() { increment(); return *this; }
        iterator operator++(int) { increment(); return *this; }
        bool operator==(iterator rhs) const { return obj_ == rhs.obj_; }
        bool operator!=(iterator rhs) const { return !(rhs==*this); }
    protected:
        void increment()
        {
            obj_->next();
            if (!obj_->valid())
                obj_ = nullptr;
        }
    };


    FileParser(Stream& stream): stream_ {&stream} {};
    iterator begin() { return iterator{this}; }
    iterator end() { return iterator{}; }
    void next() { current_ = stream_->read(); }
    bool valid() const { return stream_->valid(); }
};

因此,文件结束迭代器由指向无对象的迭代器表示。

然后您可以像这样使用它:

int main()
{
    Stream s; // Initialize it as needed
    FileParser parser {s};
    for (const std::string& token: parser)
    {
        std::cout << token << std::endl;
    }
}