Я реализовал процедуру десериализации для объекта, используя потоковый оператор <<
.Сама подпрограмма использует istreambuf_iterator<char>
для извлечения символов из потока один за другим, чтобы создать объект.
В конечном счете, моя цель состоит в том, чтобы иметь возможность перебирать поток с использованием istream_iterator<MyObject>
ивставьте каждый объект в vector
.Довольно стандартный, за исключением того, что у меня возникают проблемы с повторением istream_iterator
до stop , когда он достигает конца потока.Сейчас он просто зацикливается навсегда, хотя вызовы istream::tellg()
указывают, что я нахожусь в конце файла.
Вот код для воспроизведения проблемы:
struct Foo
{
Foo() { }
Foo(char a_, char b_) : a(a_), b(b_) { }
char a;
char b;
};
// Output stream operator
std::ostream& operator << (std::ostream& os, const Foo& f)
{
os << f.a << f.b;
return os;
}
// Input stream operator
std::istream& operator >> (std::istream& is, Foo& f)
{
if (is.good())
{
std::istreambuf_iterator<char> it(is);
std::istreambuf_iterator<char> end;
if (it != end) {
f.a = *it++;
f.b = *it++;
}
}
return is;
}
int main()
{
{
std::ofstream ofs("foo.txt");
ofs << Foo('a', 'b') << Foo('c', 'd');
}
std::ifstream ifs("foo.txt");
std::istream_iterator<Foo> it(ifs);
std::istream_iterator<Foo> end;
for (; it != end; ++it) cout << *it << endl; // iterates infinitely
}
Я знаюв этом тривиальном примере мне даже не нужен istreambuf_iterator, но я просто пытаюсь упростить проблему, так что, скорее всего, люди ответят на мой вопрос.
Итак, проблема в том, что даже если istreambuf_iterator
достигает конца буфера потока, сам фактический поток не переходит в состояние EOF
.Вызов istream::eof()
возвращает false, хотя istream::tellg()
возвращает последний байт в файле, а istreambuf_iterator<char>(ifs)
сравнивает true с istreambuf_iterator<char>()
, что означает, что я определенно в конце потока.
Я посмотрел код библиотеки IOstreams, чтобы точно определить, как он определяет, находится ли istream_iterator
в конечной позиции, и в основном он проверяет, оценивается ли istream::operator void*() const
как true
.Эта библиотечная функция istream просто возвращает:
return this->fail() ? 0 : const_cast<basic_ios*>(this);
Другими словами, она возвращает 0
(false), если бит сбоя установлен.Затем он сравнивает это значение с тем же значением в построенном по умолчанию экземпляре istream_iterator
, чтобы определить, достигли ли мы конца.
Поэтому я попытался вручную установить бит перехода в моей подпрограмме std::istream& operator >> (std::istream& is, Foo& f)
, когдаistreambuf_iterator
сравнивает истину с конечным итератором.Это сработало отлично, и правильно завершил цикл.Но сейчас я действительно растерялся.Кажется, что istream_iterator
определенно проверяет std::ios::failbit
, чтобы обозначить условие «конец потока».Но разве не для этого std::ios::eofbit
?Я думал, что failbit
был из-за ошибок, например, если базовый файл fstream
не может быть открыт или что-то в этом роде.
Итак, почему мне нужно вызвать istream::setstate(std::ios::failbit)
, чтобы получитьцикл для завершения?