0

I have a class that has two member variables and a file that in each line has to numbers. I have implemented std::istream& operator>> for that class. I would like to read this file and for each line in it create an object of my class. I do not want to manually check stream status each time I read from it, but rather I want it to throw an exception and then handle it.

My problem is, that when my code reads all the lines, but then it tries to read one more, and throws an std::fstream::failure exception. How can I avoid this, but have it be thrown when something bad actually happens (IO error)?

#include <exception>
#include <fstream>
#include <iostream>
#include <vector>

class A {
public:
  A() : m_x{0}, m_y{0} {}
  A(int x, int y) : m_x{x}, m_y{y} {}

private:
  int m_x, m_y;
};

std::istream& operator>>(std::istream& is, A& a) {
  int x, y;
  is >> x >> y;

  a = A{x, y};

  return is;
}

void load(std::vector<A>& v) {
  std::ifstream file;
  file.exceptions(std::fstream::failbit | std::fstream::badbit);

  try {
    file.open("file");
    
    // This does not work! What should I do?
    A a;
    while(file >> a) {
      v.push_back(a);
    }
  } catch (const std::fstream::failure&) {
    file.close();
    throw;
  }
}

int main() {
  std::vector<A> v;
  try {
    load(v);
  } catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
    return 1;
  }
  
  return 0;
}
Aleksander Krauze
  • 3,115
  • 7
  • 18
  • I believe you need the exception mask to be `failbit | ~eofbit` – David G Apr 24 '22 at 19:41
  • Actually, getting rid of eof might not be enough, you also have to get one value at a time in your `operator>>`, otherwise you will still get exception when first read fails and sets eofbit, then second read also sets failbit. – Yksisarvinen Apr 24 '22 at 19:43
  • @Yksisarvinen What do you mean I have to read value one at a time? It is defined as it is. The whole point of this function is so that I don't have to read values on by one. And I do not want to check for failures each time I read from stream. I want to do this only in one place, where I catch an exception. – Aleksander Krauze Apr 24 '22 at 19:49
  • _"I do not want to manually check stream status each time I read from it"_ - but that's exactly what you are doing in `while(file >> a)` so adding exceptions just makes more to code than less. Also, you should set the exception mask after opening the file – Ted Lyngmo Apr 24 '22 at 20:12
  • @TedLyngmo I don't want to check if loading failed, but I have to terminate this loop somehow. I tried to check for `file.eof()`, but it doesn't work as well. – Aleksander Krauze Apr 24 '22 at 20:16
  • What do you mean by "loading"? Opening? – Ted Lyngmo Apr 24 '22 at 20:17
  • Btw, your loop will _always_ throw an exception, even when you've read everything that can be read from the file. Is that how you want it? – Ted Lyngmo Apr 24 '22 at 20:17
  • Opening file, and reading it. All IO operations can fail. – Aleksander Krauze Apr 24 '22 at 20:18
  • Sure, then just set the exception mask after opening the file and it'll throw immediately if opening it failed. – Ted Lyngmo Apr 24 '22 at 20:18
  • @TedLyngmo That's exactly my problem! I don't want this? How can I fix this? – Aleksander Krauze Apr 24 '22 at 20:18
  • What is it that you don't want? What is "this" in _"I don't want this?"_? – Ted Lyngmo Apr 24 '22 at 20:19
  • I don't want my loop throwing always an exception. I whant it to only throw an exception when IO failed, not when I reached EOF. – Aleksander Krauze Apr 24 '22 at 20:20
  • That can't be done afaik. It's when you try to read a value after the last value in the file that eofbit will be set, but that also sets failbit. Fwiw, your current loop `while(file >> a) { v.push_back(a); }` looks perfectly fine as it is. – Ted Lyngmo Apr 24 '22 at 20:22
  • @TedLyngmo Maybe I can check in my catch block if `file.eof()` is true and if so I can ignore this exception. But this is **very** dirty hack. – Aleksander Krauze Apr 24 '22 at 20:29
  • @AleksanderKrauze I agree, that becomes even more cumbersome. I never use exceptions to deal with reading files. I define my `operator>>` and loop like you do. That's been working well for me. I would probably make `operator>>` a `friend` and read directly into the variable passed as an argument though: `std::istream& operator>>(std::istream& is, A& a) { return is >> a.m_x >> a.m_y; }` – Ted Lyngmo Apr 24 '22 at 20:32
  • If you want, you could do `if(not file.eof()) throw something;` after your loop. Seems simpler. – Ted Lyngmo Apr 24 '22 at 20:38
  • 1
    @TedLyngmo Thank you. I will reflect on this. – Aleksander Krauze Apr 24 '22 at 21:15

0 Answers0