Virtual function to file in C++ - c++

I'm just wondering if it is possible to write in a dynamic array object which consist of virtual function, display() to a text file (eg. txtfiletodisplay.txt).
edited:
instead of using vector, im implementing it this way.
in main()
clsStaff** objStaff = new clsStaff[userInput];
ofstream outputFile("staff.txt");
for (x=0; x<userInput; x++)
outputFile = objStaff[x]->display();

class FileDisplayer
{
public:
virtual File display() const;
...
};
class File
{
...
};
std::vector<FileDisplayer*> objStaff; // this
FileDisplayer* objStaff[100]; // ... or this
FileDisplayer** objStaff; // ... or this
File outputFile = objStaff[x]->display();
No idea what you really mean, so FileDisplayer and File might be misleading names.
EDIT:
It might be that display is meant to take outputFile as a parameter (and outputFile is an output stream). In that case the above should be rewritten like this
class FileDisplayer
{
public:
virtual void display(std::ostream& file) const;
...
};
std::vector<FileDisplayer*> objStaff; // this
FileDisplayer* objStaff[100]; // ... or this
FileDisplayer** objStaff; // ... or this
std::ofstream outputFile;
...
objStaff[x]->display(outputFile);

Related

c++ Read/Write class from/to binary file

I need to write a class to a binary file, and then I need to read it back.
I have Triangle and BinaryFile classes, and some other classes. I am not sure if I am writing incorrectly or reading incorrectly. An error occurs when reading. After debugging, I think that it gets inappropriate data for my private variables. I will be very glad if someone can give me some advice on how to make it work properly.
I wasn't sure if I should paste the whole code or not, so I will give you a short snippet of code. Just in case, here is a download link for my source code:
https://my.pcloud.com/publink/show?code=XZJ7CYZbsLWLglqV5p83csijcEUTFqqpM3k
I am a newbie in programming and I don't speak English very well, so I apologize in advance for my mistakes.
class Point
{
private:
int x;
int y;
};
class Figure
{
private:
string name;
string type;
};
class Triangle: public Figure
{
private:
Point p1, p2, p3;
};
class BinaryFile
{
private:
string FileName;
fstream File;
public:
//...
void AddNewFigure(istream& stream)
{
File.open(this->FileName, ios::binary | ios::app);
if(!this->File)
{
cerr<<"File error <"<<this->FileName<<">\n";
exit(1);
}
Triangle fig;
fig.MakeNewFigure(stream);
File.write((char*)&fig, sizeof(Triangle));
File.close();
}
Triangle GetTriangle()
{
Triangle trig;
Point p;
string str(""); int x(0);
File.open(this->FileName, ios::binary | ios::in);
if(!this->File)
{
cerr<<"File error <"<<this->FileName<<">\n";
exit(1);
}
File.read((char*)&trig, sizeof(Triangle));
File.close();
return trig;
}
};
The answer depends on whether you are just doing this to learn how files work or whether saving to the file is just incidental and you don't care how it works.
If you just want to get the stuff to save and restore and you don't care how it works then use a third party library. There are many many of them.
If you want to learn how to read and write things to files then you will need to make your own read and write functions. I have made a sample program that will explain how it works:
#include <string>
#include <fstream>
#include <iostream>
class Point
{
private:
int x;
int y;
public:
Point():x(0),y(0){}
Point(int x,int y):x(x),y(y){}
void write(std::ostream& f)
{
// We can just write out the bytes for x and y because
// they are primitive types stored in the class
f.write( (char*)&x, sizeof(x) );
f.write( (char*)&y, sizeof(y) );
}
void read(std::istream& f)
{
// We can just read the bytes directly into x and y because
// they are primitive types stored in the class
f.read( (char*)&x, sizeof(x) );
f.read( (char*)&y, sizeof(y) );
}
};
class Figure
{
private:
std::string name;
std::string type;
public:
Figure(){}
Figure(std::string name,std::string type):name(name),type(type){}
void write(std::ostream& f)
{
size_t size;
// we need to store the data from the string along with the size
// because to restore it we need to temporarily read it somewhere
// before storing it in the std::string (istream::read() doesn't
// read directly to std::string)
size = name.size();
f.write( (char*)&size, sizeof(size_t) );
f.write( (char*)name.c_str(), size );
size = type.size();
f.write( (char*)&size, sizeof(size_t) );
f.write( (char*)type.c_str(), size );
}
void read(std::istream& f)
{
size_t size;
char *data;
// when we read the string data we need somewhere to store it
// because we std::string isn't a primitive type. So we read
// the size, allocate an array, read the data into the array,
// load the std::string, and delete the array
f.read( (char*)&size, sizeof(size) );
data = new char[size+1];
f.read( data, size );
data[size]='\0';
name = data;
delete data;
f.read( (char*)&size, sizeof(size) );
data = new char[size+1];
f.read( data, size );
data[size]='\0';
type = data;
delete data;
}
};
class Triangle: public Figure
{
private:
Point p1, p2, p3;
public:
Triangle(){}
Triangle(Point x,Point y,Point z,Figure f):p1(x),p2(y),p3(z),Figure(f){}
void write(std::ostream& f)
{
// First write the base class then write the members of this class
Figure::write(f);
p1.write(f);
p2.write(f);
p3.write(f);
}
void read(std::istream& f)
{
// First read the base class then read the members of this class
Figure::read(f);
p1.read(f);
p2.read(f);
p3.read(f);
}
};
class BinaryFile
{
private:
std::string FileName;
std::fstream File;
public:
BinaryFile(std::string FileName) : FileName(FileName) {};
void WriteTriangle()
{
File.open(FileName, std::ios::binary | std::ios::out);
if(!File)
{
std::cerr<<"File error <"<<FileName<<">\n";
exit(1);
}
Triangle trig({1,2},{3,4},{5,6},{"name","type"}); // something new
trig.write(File);
File.close();
}
Triangle ReadTriangle()
{
File.open(FileName, std::ios::binary | std::ios::in);
if(!File)
{
std::cerr<<"File error <"<<FileName<<">\n";
exit(1);
}
Triangle trig; // default values
trig.read(File);
File.close();
return trig;
}
};
main()
{
BinaryFile bin("file.bin");
bin.WriteTriangle();
Triangle trig = bin.ReadTriangle();
// at this point trig has the values we stored
return 0;
}
It's not easy to reproduce the error, due to your large source code and missing data file. But a quick inspection shows that you read and write the binary data using bloc operations:
Triangle trig;
...
File.read((char*)&trig, sizeof(Triangle));
Unfortunately this kind of approach only works if the object you want to save/load is of a class that is trivially copyable, as the following code will demonstrate:
if (is_trivially_copyable<Triangle>::value)
cout << "Triangle is trivially copyable" << endl;
else cout << "Triangle is not trivially copyable" << endl;
So you'll have to serialize the object content writing field by field instead of using a bloc operation. This FAQ on serialization should help you to consider the alternatives.
What you are looking for is to serialize your classes/data that should be saved to file. There are several libraries that has been optimized regarding time and memory consumption for this. Would you mind using a 3rd party library?
If not, have a look at for example boost serialization, cereal or maybe even Google's ProtoBuf. I recon Cereal is a good start if you are using C++11.
If you'd like to write your own serialization you'd have to consider that for every object that has a dynamic size (such as a string), you will also need to save the object's size to the file. For more info please have a look here:
https://stackoverflow.com/a/11003590/5874704

Writing to a file using a helper class interface, where do I open the ofstream?

It's kind of a difficult question to put into a single title. So I'm trying to write the data contained in an Account class out to a file using a member function of a FileIO class. FileIO is a composite member data object of the Account class. I have 3 Account objects stored in a vector of pointers, which I am looping through to write all of them out to a file. I'm wondering how I get the WriteData() function to see the open file? I can't open the file within the function itself, because I need all the Account objects to be written out to the file before closing it.
Here's my code:
class FileIO
{
private:
ofstream _accoFile;
public:
FileIO();
~FileIO();
void WriteData(Account*);
ofstream& GetoStream();
};
void FileIO::WriteData(Account* acc)
{
_accoFile << acc->GetAccNum() << "\n" << acc->GetAccOwner()->GetName() << "\n" << acc->GetAccOwner()->GetAddress() << "\n" << acc->GetAccBal() << "\n";
//acc->WriteAcc(_accoFile);
}
class Account
{
private:
Person* _accOwner;
int _accNumber;
double _accBalance;
FileIO* _iFile;
public:
Account();
~Account();
Account(Person*, int, double);
Person* GetAccOwner();
int GetAccNum();
double GetAccBal();
FileIO* GetiFile();
};
In main:
FileIO* test = new FileIO();
test->GetoStream().open("accInfo.txt");
for (int i = 0; i < accountsVec.size(); i++) {
accountsVec[i]->GetiFile()->WriteData(accountsVec[i]);
}
test->GetoStream().close();
What makes it difficult to answer your question is that the real issue lies elsewhere:
FileIO is a composite member data object of the Account class.
For proper separation of responsibilities, FileIO shouldn't be a member of Account.
Instead, it should be a separate class. With this SOLID-type design, the problem of opening the ofstream is easily solved, as this could be done e.g. in the constructor:
class FileIO
{
private:
ofstream _accoFile;
public:
FileIO(const std::string& filename) : _accoFile(filename) {};
void WriteData(Account*);
};
With this design:
The output file is opened as soon as you create an instance of FileIO;
Several instances of Account can be serialized into the same output file;
The output file is automatically closed as soon as the FileIO instance is destroyed.
Below is a simple main demonstrating how such a serializer could be used:
int main()
{
std::vector<Account*> accountsVec;
FileIO writer("accInfo.txt");
for (int i = 0; i < accountsVec.size(); i++) {
writer.WriteData(accountsVec[i]);
}
return 0;
}
UPDATE
I understand the assignment demands a common member serializer for all Account instances. That's really weird, so it might be worth double-checking. In any case, here is what I could come up with, based on these bizarre requirements.
The serializer could be designed with the typical I/O open and close methods:
class FileIO
{
private:
ofstream _accoFile;
public:
void open(const string& filename) { _accoFile.open(filename); }
void close() { _accoFile.close(); }
void WriteData(Account*);
};
Since the serializer is a compound member of Account, it should be integrated into the Account ctor:
Account::Account(Person* accOwner,int accNumber,double accBalance,FileIO* iFile)
: _accOwner(accOwner), _accNumber(accNumber), _accBalance(accBalance), _iFile(iFile)
{}
If Account is given the responsibility of saving itself into a target file, it's better to write an Account method that implements that responsibility:
void Account::WriteData()
{
_iFile->WriteData(this);
}
Below is an example of a main based on this design:
int main()
{
FileIO fileio;
Person person1{"toto","Paris"};
Person person2{"tutu","London"};
Person person3{"tata","Lisboa"};
vector<Account> accountsVec;
accountsVec.emplace_back( &person1 , 1, 12., &fileio );
accountsVec.emplace_back( &person2 , 2, 100., &fileio );
accountsVec.emplace_back( &person3 , 3, 1000., &fileio );
fileio.open("accInfo.txt");
for (int i = 0; i < accountsVec.size(); i++) {
accountsVec[i].WriteData();
}
fileio.close();
}

Can I get the name of file used from ifstream/ofstream?

I need to know if there exists a method in ifstream so I can get the name of the file tied to it.
For instance
void some_function(ifstream& fin) {
// here I need get name of file
}
Is there a method in ifstream/ofstream that allows to get that?
As mentioned there's no such method provided by std::fstream and it's derivates. Also std::basic_filebuf doesn't provide such feature.
For simplification I'm using std::fstream instead of std::ifstream/std::ofstream in the following code samples
I would recommend, to manage the underlying file name in a little helper class yourself:
class MyFstream {
public:
MyFstream(const std::string& filename)
: filename_(filename), fs_(filename) {
}
std::fstream& fs() { return fs_; }
const std::string& filename() const { return filename_; }
private:
std::string filename_;
std::fstream fs_;
};
void some_function(MyFstream& fin) {
// here I need get name of file
std::string filename = fin.filename();
}
int main() {
MyFstream fs("MyTextFile.txt");
some_function(fs):
}
Another alternative,- if you can't use another class to pass to some_function() as mentioned above -, may be to use an associative map of fstream* pointers and their associated filenames:
class FileMgr {
public:
std::unique_ptr<std::fstream> createFstream(const std::string& filename) {
std::unique_ptr<std::fstream> newStream(new std::fstream(filename));
fstreamToFilenameMap[newStream.get()] = filename;
return newStream;
}
std::string getFilename(std::fstream* fs) const {
FstreamToFilenameMap::const_iterator found =
fstreamToFilenameMap.find(fs);
if(found != fstreamToFilenameMap.end()) {
return (*found).second;
}
return "";
}
private:
typedef std::map<std::fstream*,std::string> FstreamToFilenameMap;
FstreamToFilenameMap fstreamToFilenameMap;
};
FileMgr fileMgr; // Global instance or singleton
void some_function(std::fstream& fin) {
std::string filename = fileMgr.getFilename(&fin);
}
int main() {
std::unique_ptr<std::fstream> fs = fileMgr.createFstream("MyFile.txt");
some_function(*(fs.get()));
}
No. C++ streams do not save the name or the path of the file.
but, since you need some string to initialize the stream anyway, you can just save it for future use.
No, such a method does not exist.

ifstream variable in class

I have my class which has to have ifstream file in it.
I dont know how to present it in the class header
A:
class MyClass
{
...
ifstream file;
...
}
B:
class MyClass
{
...
ifstream& file;
...
}
I know that ifstream has to get path in the decaleration, so how do I do it?
Also how do I open a file with it?
EDIT:
I want the first way, but how do I use it SYNTAX-ly?
let's say this is the header(part of it)
class MyClass
{
string path;
ifstream file;
public:
MyClass();
void read_from_file();
bool is_file_open();
...
}
funcs
void MyClass::read_from_file()
{
//what do I do to open it???
this->file.open(this->path); //Maybe, IDK
... // ?
}
You more than likely want the first option. The second is a reference to some other ifstream object, rather than an ifstream that belongs to MyClass.
You don't need to give an ifstream a path immediately. You can later call the ifstream's open function and give that a path. However, if you want to open the ifstream immediately on initialisation, you need to use the constructor's initialisation list:
MyClass() : file("filename") { }
If you need the constructor to take the file name, simply do:
MyClass(std::string filename) : file(filename) { }
Initialise it in the constructor:
class my_class {
public:
my_class(char const* path) : file(path) {
}
my_class(std::string const& path) : my_class(path.c_str()) {
}
private:
std::ifstream file;
};
Also see The Definitive C++ Book Guide and List.

OO C++ - Virtual Methods

Just a really quick question here. I'm using virtual functions to read in from a text file. Now, it's virtual because in one aspect I want the values to be normalised, and, in the other respect I don't want them to be normalised. I have tried to do this:
bool readwav(string theFile, 'native');
So in theory, if the 'native' is used, this method should be called, however, if 'double' is called then a different version of the method is called. Same for if the value is empty, it should just perform the native option.
First question, why doesn't the declaration above work? Also, is this the best route to go down? Or, would it be better to have just one class method that switches between the options.
Thanks :)
Update:
Where am I going wrong?
bool Wav::readwav(string theFile, ReadType type = NATIVE)
{
// Attempt to open the .wav file
ifstream file (theFile.c_str());
if(!this->readHeader(file))
{
cerr << "Cannot read header file";
return 0;
}
for(unsigned i=0; (i < this->dataSize); i++)
{
float c = (unsigned)(unsigned char)data[i];
this->rawData.push_back(c);
}
return true;
}
bool Wav::readwav(string theFile, ReadType type = DOUBLE)
{
// Attempt to open the .wav file
ifstream file (theFile.c_str());
cout << "This is the double information";
return true;
}
Because 'native' is a multi-char character, not a string. I'd go with multiple versions of the function though:
bool readwavNative(string theFile);
bool readwavDouble(string theFile);
or at least an enum as the second parameter:
enum ReadType
{
ReadNative,
ReadDouble
};
//...
bool readwav(string theFile, ReadType type);
Sounds like what you want is an enumeration with a default parameter.
enum FileType
{
NATIVE=0,
DOUBLE
};
bool readwav(string theFile, FileType type = NATIVE);
Default parameters are present in the function declaration, do not put them in the definition.
bool readwav(string theFile, FileType type)
{
switch(type)
{
case NATIVE: { ... } break;
case DOUBLE: { ... } break;
default: { ... } break;
}
}
This way, calling readwav without a parameter will use the NATIVE type by default.
readwav("myfile.wav"); // Uses NATIVE type
readwav("myfile.wav", NATIVE); // Also uses NATIVE
readwav("myfile.wav", DOUBLE); // Uses DOUBLE type
The question has oop in it so I would assume an oop answer is wanted. I think a strategy patter would suit your purpose.
class WavReader
{
public:
WavReader(const std::string fileName)
{
//open file and prepare to read
}
virtual ~WavReader()
{
//close file
}
virtual bool read()=0;
};
class NativeWavReader: public WavReader
{
public:
NativeWavReader(const std::string fileName): WavReader(fileName){}
virtual bool read()
{
//native reading method
std::cout<<"reading\n";
return true;
}
};
NativeWavReader implements the read method from the strategy WavReader, if you want another method you create a class OtherWavReader reading the file differently.