Search code examples
c++istream

Read file to istream replacing class variables


I understand how to implement a overloaded ostream operator but I'm a little confused on a overloaded istream operator. All the examples online only show a brief demonstration (i.e. is >> val; ) which doesn't help me in context with my class variables and methods. In my main, I'm trying to read in a file and replace the pointers contents with the file contents. I have a factory method and read method that does that already but I'm trying to overload my istream operator to do the same. However, although the pointers contents are replaced, I still receive the cerr message below. Am I supposed to pass something to the ifs in my >> function?

Here is my main

int main() {

  // factory method instantiates all class variables
  Base *aa = Base::create(file);     // it works

  ifstream in(file2);
  if((in >> *aa).fail())             // Read file contents
    cerr << "Read failed" << '\n'; 

  cout << *aa;                       // prints vector contents
}

Prints

// file     // file2      cout << *aa     SupposedToBe
555         111           Read Failed     111
555         222           111             222
555         333           222             333
                          333

What I have so far (.cpp)

istream &operator>>(istream &ifs, Base &val) {

  string line;
  getline(ifs, line); // type of file

  if(line == "type1") 
    val.filetype = "type1";
  if(line == "type2")
    val.filetype = "type2";

  val.vec.clear();    // clear old vector
  vector<int> inner;  // inner vec to push onto main vec

  // read remaining contents
  while(getline(ifs, line)) {

    for(size_t i = 0; i < line.length(); i++) 
       inner.push_back(line[i]);

    val.vec.push_back(inner);
    inner.clear();
  }

  val.height = val.vec.size();
  val.width = val.vec[0].size();
  val.max = val.vec[height-1][width-1];

  return ifs;
}

My overloaded ostream function looks a lot different that what is displayed above (i.e. lots of os << value). But, whenever I do ifs >> value, I get all kinds of compiler errors (i.e. I tried just doing ifs >> val.image; ifs >> val.height; etc. but all I got was compiler errors). What am I supposed to pass to the istream (in the above function) so the error above (below main) doesn't display.

.h file for reference

class Base 
{
  protected:
    std::vector<std::vector<int> > vec;
    std::string filetype;
    int width, height, max;
    Base() = default;
    Base &operator=(const Base &) = default;
  public:
    static Base* create(std::string filename);
    virtual ~Base();
    // read by derived class
    virtual void read(std::string filename) = 0;
    friend std::istream &operator>>(std::istream &, Base &);
    friend std::ostream &operator<<(std::ostream &, const Image &);
};

Solution

  • Use of

    if((in >> *aa).fail())             // Read file contents
       cerr << "Read failed" << '\n';
    

    is a problem. The way you have implemenented operator>>(std::istream &, Base &), that logic is bound to fail every time.

    You have a loop in the function

    while(getline(ifs, line)) {
       ...
    }
    

    That loop will break only when there is nothing to read from the file or there is an error in reading the contents of the file. When that loop exits, ifs.fail() will always be true.

    You should replace the if statement with just:

    in >> *aa;
    

    If you need to do any error checking and print appropriate error messages, that has to be done inside operator>>(std::istream &, Base &).

    Update, in response to OP's comment

    One way to make sure that the istream does not get to the point where ifs.fail() is always true is to know what's expected ahead of time.

    If the number of lines expected in the file is the first input, then, it is possible to read all the data and return from the function such that ifs.fail() is false.

    Sample input:

    type1
    3
    111
    222
    333
    

    Then, the oprator>> function can be defined as:

    std::stream& operator>>(std::istream& ifs, Base& val) 
    {
       std::string line;
       if ( !getline(ifs, line) ) // type of file
       {
          // Problem reading. No point trying to read more.
          return ifs;
       }
    
       if(line == "type1") 
          val.filetype = "type1";
       if(line == "type2")
          val.filetype = "type2";
    
       // read remaining contents
    
       int numLines = 0;
       if ( !(ifs >> numLines) )
       {
          // Problem reading. No point trying to read more.
          return ifs;
       }
    
       val.vec.clear();    // clear old vector
       vector<int> inner;  // inner vec to push onto main vec
    
       for ( int n = 0; n < numLines; ++n )
       {
          if ( !getline(ifs, line))
          {
             // Problem reading. No point trying to read more.
             return ifs;
          }
    
          for(size_t i = 0; i < line.length(); i++) 
          {
             inner.push_back(line[i]);
          }
    
          val.vec.push_back(inner);
          inner.clear();
       }
    
       val.height = val.vec.size();
       val.width = val.vec[0].size();
       val.max = val.vec[height-1][width-1];
    
       return ifs;
    }