Initializing private istream data member in C++ - c++

I'm currently trying to initialise a private istream variable in class.
The class definition looks like:
#define PARSER_H
class parser {
public:
parser();
parser(string predict_table_file_name);
private:
int getMaxRHS(string predict_table_file_name);
int getMaxPairs(string predict_table_file_name);
int getMaxPairsY(string predict_table_file_name);
int getMaxRHSY(string predict_table_file_name);
int getMaxSymbols(string predict_table_file_name);
int getGoalSymbol(string predict_table_file_name);
int getNumberOfTerminalSymbols(string predict_table_file_name);
string getSymbol(int symbolID);
string getToken();
string openFile(string sourceFile);
bool isTerminalSymbol(string token, string symbolArray[], int terminalSymbols);
istream scanFile;
};
#endif
The variable in question is "istream scanFile". The code I'm using to try and initialize it looks like this.
string parser::openFile(string sourceFile) {
filebuf fb;
fb.open(sourceFile.c_str(), ios::in);
parser::scanFile(&fb);
}
The line "parser::scanFile(&fb);" is giving me the trouble. Apparently the compiler thinks I'm trying to call function, which I guess I am, but I just want to call the constructor on parser::scanFile.
I'm new-ish to C++, so any help would be greatly appreciated.

To solve your question you can add the filebuf as a member variable.
class parser
{
// STUFF LIKE BEFORE
filebuf fb;
istream scanFile;
};
parser::parser()
:fb()
,scanFile(&fb)
{}
string parser::openFile(string sourceFile)
{
fb.open(sourceFile.c_str(), ios::in);
}
But you should probably be using an fstream object:
class parser
{
// STUFF LIKE BEFORE
ifstream scanFile;
};
parser::parser()
:scanFile()
{}
string parser::openFile(string sourceFile)
{
scanFile.open(sourceFile.c_str());
}
see: http://www.cplusplus.com/reference/iostream/fstream/

You must do such initialization in the initialization list of the constructor. Why do you want to implement openFile as a separate function?
Also, if scanFile is used for reading from files, why not use ifstream instead?
Are you going to create parsers that are not associated with files?
If not, try this for the constructor:
parser(string sourceFile) : scanFile(sourceFile.c_str()) { ... other constructor stuff ... }
That assumes that scanFile is a ifstream. Using a istream is more complex. Your original code would not have worked, because the filebuf is destroyed when you exit the openFile function.

You can't, because the object is already constructed. But you can
scanFile.rdbuf( &fb );
but that won't do either, because fb will be destructed as soon as you return from the openFile. But you'll have to resolve this problem yourself, because it's your code flow.

You want something like:
parser::scanFile = istream(&fb);
to invoke the constructor.

Related

ifstream as class member [duplicate]

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.
}
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)
I think a better solution would be for you to:
Construct a ifstream first.
Construct a Parser using the ifstream object.
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) ... {}
...
};
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_)

How to have a file stream as a class member

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.
}
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)
I think a better solution would be for you to:
Construct a ifstream first.
Construct a Parser using the ifstream object.
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) ... {}
...
};
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_)

No members available for object declared as a class variable

I'm having a little bit of a hard time explaning the problem, so here's a simple rundown of my code:
Imagine I have a class called 'character'
#include "myEnums"
#include "weapon"
character {
protected:
string characterName;
weapon* myWeapon;
public:
string getCharacterName();
void setCharacterName( string );
string getMyWeapon();
void setMyWeapon();
}
Then within 'setMyWeapon' I use this simplified code.
void character::setMyWeapon() {
this->myWeapon = new weapon("longsword");
//this->myWeapon = new weapon(myEnums::LONGSWORD); //Ideally this
}
string getMyWeapon() {
return this->myWeapon.tostring();
}
But when I type the '.' for 'myWeapon' there's no members, anyone know whatup? Assume 'tostring' is defined in 'weapon.h'...
Since myWeapon is a pointer, you need to dereference it to access the pointee's members:
myWeapon->tostring()
// ^^^^

C++ Instantiating member classes on the stack using different parts of single input byte file

I have a class that has several member classes as attributes. The constructor of the class will take a filename of a byte file. The different member classes use subsequent parts of the file in their constructors, lets call them part A, B and C. The size of the file will vary.
Using the heap I would do something like this:
class myClass
{
myClass(char *filename)
{
std::ifstream inputFile(filename, std::ios::binary);
m_Class1 = new ClassA(inputFile); // read part A
m_Class2 = new ClassB(inputFile); // read part B
m_Class3 = new ClassC(inputFile); // read part C
inputFile.close();
}
}
I would like to do this on the stack instead of the heap.
Initialization lists come to mind, but for that I would have to waste time re-reading the redundant part of the inputfile to get to the part needed for each member class.
I don't know if this is just a terrible approach (most likely), but does anyone have any suggestions towards accomplishing this effectively? Or suggestions for another way of organizing this?
class myClass {
myClass(char *filename) : inputFile(filename, std::ios::binary),
m_Class1(inputFile),
m_Class2(inputFile),
m_Class3(inputFile) {
inputFile.close();
}
private:
std::ifstream inputFile;
ClassA m_Class1;
ClassA m_Class2;
ClassA m_Class3;
};
Note that the declaration order of the members is important.
You can use the following, just slightly changed syntax. It's not "on the stack" but it doesn't use the heap, so might be good for you.
class myClass
{
public: // note: public constructor to make the class usable
myClass(char *filename)
{
std::ifstream inputFile(filename, std::ios::binary);
m_Class1 = ClassA(inputFile); // read part A
m_Class2 = ClassB(inputFile); // read part B
m_Class3 = ClassC(inputFile); // read part C
inputFile.close(); // note: no need to close; C++ does this automatically
}
private:
ClassA m_Class1; // note: not a pointer
ClassB m_Class2;
ClassC m_Class3;
}
This requires ClassA, B and C to have default constructors and assignment operators (either default ones or those you have coded).

C++ string member construction

How do I go about using the constructor for a member which is a string? Here is an example (which is wrong I realize)
class Filestring {
public:
string sFile;
Filestring(const string &path)
{
ifstream filestream(path.c_str());
// How can I use the constructor for the member sFile??
// I know this is wrong, but this illustrates what I want to do.
string sFile((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>());
}
};
So basically I want to be able to use the member sFile's constructor without doing a string copy. Is there a way to achieve this through assignment?
The best you can use is string::assign:
sFile.assign(istreambuf_iterator<char>(filestream), istreambuf_iterator<char>());
But in C++11 there is a move assignment operator for string so doing the following is almost as efficient (there is no copy of character data, the character data are moved):
sFile = string(istreambuf_iterator<char>(filestream), istreambuf_iterator<char>());
Yet another trick when C++11 move assignment is not avaliable is to use std::swap or string::swap. The efficiency would be probably almost identical to the move assignment variant.
string newContent(istreambuf_iterator<char>(filestream), istreambuf_iterator<char>());
std::swap(sFile, newContent); // or sFile.swap(newContent);
One way is to make filestream a member, so it can be referred to in the constructor initialization list:
class Filestring {
private:
ifstream filestream;
public:
string sFile;
Filestring(const string &path) : filestream(path.c_str()), sFile((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>())
{}
};
For this to work properly, filestream must appear in the class definition before sFile, or else it won't be initialized in time to use it in the sFile constructor. Of course, you're also adding to the overhead of the Filestring class.
A safer technique which also avoids the overhead of string copying is to use string::swap():
class Filestring {
public:
string sFile;
Filestring(const std::string &path)
{
std::ifstream filestream(path.c_str());
sFile.swap(string((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>()));
}
};
Not really, you're bound to do a copy if you don't initialize members inside the initializer list, which in your case doesn't seem possible.
Note that your code doesn't initialize the member, but creates a new local variable.
The proper way would be
sFile = std::string((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>());
You can do it.
But its not worth the extra complexity. If you want to avoid the cost of a copy (which a decent compiler will probably do anyway). You can load it into a temporary then use std::swap().
I would do:
class Filestring {
public:
string sFile;
Filestring(const string &path)
{
ifstream filestream(path.c_str());
string data((istreambuf_iterator<char>(filestream)), istreambuf_iterator<char>());
// Just swap the internal data structures:
std::swap(sFile, data);
}
};
Use initialization lists:
Filestring(const string &path):sFile(/*what you're initializing with*/)
{//more
}
sFile.assign(constructor args);