1

I have the following parser class that works in Visual C++

class Parser
{
   private:
   const char* filename;
   std::ifstream filestream;
   std::vector<std::string> tokens;
   unsigned int linect;

   public:
   Parser(const char* filename);
   bool readline();
   std::string getstrtoken(unsigned int i) const { return tokens[i]; }
   int getinttoken(unsigned int i) const { return atoi(tokens[i].c_str()); }
};

Parser::Parser(const char* filename) :
   filename(filename),
   linect(0)
{
   filestream = ifstream(filename); // OK in VC++, not with GCC?
}

bool Parser::readline()
{
   std::string line;
   getline(filestream, line);
   std::stringstream ss(line);
   std::string token;

   tokens.clear();
   while(getline(ss, token, ' ')){ if(token != "") tokens.push_back(token); }
   linect++;
   return (filestream != NULL);
}

But when I try to compile it with GCC 4.8.2, I get errors saying that I cannot assign to filestream. From what I read elsewhere on this site, you can do

std::ifstream filestream(filename);

but you cannot do

std::ifstream filestream;
filestream = ifstream(filename);

which is essentially what I need to do if I want to declare filestream as a member of the Parser class and initialize it in the constructor.

I would like to have the file stream kept within the Parser class so that those who use the parser don't need to declare and keep track of it. It seems to me that this should be self-contained in the Parser class since its internal methods (e.g. readline()) are the only ones that use it.

Is there a way to achieve this that works with both platforms?

Thanks.

edit: My fix was to explicitly call the open() method of ifstream. My parser class constructor now looks like:

Parser::Parser(const char* filename) :
   filename(filename),
   linect(0)
{
   filestream.open(filename);
   // Do some checking to make sure the file exists, etc.
}
tomocafe
  • 1,425
  • 4
  • 20
  • 35

3 Answers3

2

You can't, since std::ifstream has deleted copy constructor and copy assignment. You may get around by doing

filestream.swap(ifstream(filename)).

The fact that it compiles on visual studio is mostly because it gets inlined into either move assignment or move constructor(I'm not so good to tell you which exactly). If you try

std::ifstream myF;
filestream = myF;

it won't compile.

However you may try to do the move I wrote, or you can just call .open(http://en.cppreference.com/w/cpp/io/basic_ifstream/open)

Creris
  • 1,128
  • 9
  • 20
  • I just added `filestream.open(filename)` to the body of the `Parser` constructor. I forgot that you could open a file outside of the stream's constructor... doh! – tomocafe Jul 15 '14 at 21:13
2

I think a better solution would be for you to:

  1. Construct a ifstream first.
  2. Construct a Parser using the ifstream object.
  3. Change Parser to store a reference to an istream object. This allows you the flexibility of being able parse the contents of a file, stdin, and a string.

class Parser
{
   private:
      std::istream& str;
      std::vector<std::string> tokens;
      unsigned int linect;

   public:
      Parser(std::istream& s) : str(s) ... {}

      ...
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

std::ifstream don't have a copy constructor, probably one of the many extensions of VC++. Correct code is:

Parser::Parser(const char* filename) :
   filename(filename),
   linect(0),
   filestream(filename)
{
}

Please take note of member variable and parameter filename. Use this-> or change name (recommended, normally prefix is used for member variables _ or m_)

NetVipeC
  • 4,402
  • 1
  • 17
  • 19
  • what if string is defined before the fstream? wont that potentionally let `filestream` empty, since the member variables are constructed in their order of declaration(afaik)? – Creris Jul 15 '14 at 21:06
  • Interesting, albeit a bit confusing. I will go with the other option. – tomocafe Jul 15 '14 at 21:10
  • in inline initialization i tried to use always parameters variables to avoid this type of errors, by yes, member variables are initialized in the same order as they are declared, not in the order used in inline initialization, GCC with option -Wall warn about this. The problem in this case is that **member name == parameter name** – NetVipeC Jul 15 '14 at 21:12