C++自定义惰性迭代器

C++ custom lazy iterator

本文关键字:迭代器 自定义 C++      更新时间:2023-10-16

>我有一个比较简单的文本文件解析器。我解析的文本被分成用{ 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
}

这变得非常混乱,尤其是当我在块内有块时。为了支持块内的块,迭代器必须全部引用同一读取器的偏移量,以便内部块将推进偏移量,外部块将从该高级偏移量重新开始迭代(在内部完成之后)。

这有可能在C++实现吗?

为了在 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;
}
}