I'm creating two functions to save and load a object into the program.
The save function aparently saves the object successfully:
void Game::SaveGame()
{
std::string filename = "saves/miner-save-" + currentDate() + ".gem";
std::ofstream outfile;
outfile.open(filename.c_str(), std::ios::binary);
outfile.write((char *)(&myGame), sizeof(Game));
if (outfile.fail())
std::cout << "write failed" << std::endl;
outfile.close();
std::cout << myGame->myMiner->getX(); //debug purposes : prints 25
}
The problem seems to be in the load function, because the value returned from a integer of the object that is read is incorrect.
void Game::LoadGame()
{
..some code..
std::string filename = GetLoadFilename(nameID); //works
std::ifstream infile;
infile.open(filename, std::ios::binary);
infile.read((char *)&myGame, sizeof(Game));
if (infile.fail())
{
std::cout << "read failed" << std::endl;
}
std::cout << myGame->myMiner->getX(); //debug purposes : prints -842150451
Play(myGame->myMiner->getX(), myGame->myMiner->getY());
}
Class Game:
class Game {
Game *myGame;
Miner *myMiner;
BlockTypes *myBlockTypes;
//Block* myBlocks[10000];
Consola *myConsole;
Mine *myMine;
int linhas, colunas;
int _currentStatus;
public:
..some functions such as load & save
};
When loading, the "read failed" message is always shown, whats wrong ?
Writing a struct with pointers writes the value of the pointers, not the value of the pointed objects.
You need to look into serialization. Serialization is a way to structure data onto a continuous unstructured medium like a hard drive. ie, how structure your info into a line of bits so you'll be able to reconstruct your objects from that same line of bits. the previously linked boost library is an option, but there is also XML, JSON and other solutions that you should check out.
The general way is to use "infile >> myGame". In the Game class, define operator<< and operator>> to override the defaults with versions customized for your Game class.
These functions generally have a list of >> or << for each member of the class (which is the default if you don't define the operators). But you need to make some decisions and write some code for the pointer type members. The locations of objects in memory will move from run to run, so you can't just save and load pointer values. For instance, maybe in Game::operator<< you need to call << on myMine to dump it (deep copy). But maybe Miner is a pointer into an array that is saved and loaded separately, so Game::operator<< saves a miner id number or name string and Game operator>> uses the saved data to look up the up the Miner to get the pointer.
Related
so basically I was trying to save a class inside a .dat file but in my code but it says this error No matching member function for call to 'open' but I put fstream header. I don't know if I'm writing something wrong. I use Xcode 10.
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
memberinformation()
{ name="not assigned" ;
phonenumber=0;
memberid=0;
}
int option3();
int option2();
int option1();
int option4();
};
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app,ios::binary) //this is where I get the error.
f.write((char*)&k,sizeof(k));
}
You are lucky to have been stopped by a simple error. #Alex44 has already shown how to get rid of the error:
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
But the following line is even worse:
f.write((char*)&k,sizeof(k));
because the compiler will not show any error, while the content of the string will not be saved in the file. std::string is not trivially copiable and because of that, the memberinformation class is not either. So you should not try to write it to a file as raw bytes.
You should instead write a serialization function that writes to a binary stream (just a possible serialization way):
phonenumber as a long int (no problem there)
memberid as an int (no problem there)
name.size as a size_t
name.data as name.size bytes
The other two answers have answered:
Why its not compiling.
Why its a bad idea to write binary objects.
I would suggest that you serialize the object via the standard C++ technique of using the stream operators. This makes writting/reading the objects trivial and usually makes debugging problems easy.
Using the format suggested by #serge-ballesta in his post:
class memberinformation
{
string name; //name
long int phonenumber; // phone number
int memberid; // member id
public :
// OLD STUFF GOES HERE
void swap(memberinformation& other) noexcept
{
using std::swap;
swap(name, other.name);
swap(phonenumber, other.phonenumber);
swap(memberid, other.memberid);
}
friend std::ostream& operator<<(std::ostream& str, memberinformation const& data)
{
return str << data.phonenumber << " "
<< data.memberid << " "
<< data.name.size() << " "
<< data.name << " ";
}
friend std::istream& operator<<(std::istream& str, memberinformation& data)
{
memberinformation tmp;
std::size_t nameSize
if (str >> tmp.phonenumber >> tmp.memberid >> nameSize) {
// All sizes were read correctly.
tmp.name.resize(nameSize);
if (str.ignore(1).read(&tmp.name[0], nameSize)) {
// ignored the space and read the name correctly.
// So now we update the "data" object
tmp.swap(data);
}
}
return str;
}
};
Now in your code:
int main()
{
memberinformation object;
std::cout << object;
std::cin >> object;
std::ofstream file("Data.dat");
file << object;
}
You miss a semicolon and you need to "bitwise or" your flags:
void wrt_file() //file function
{
memberinformation k;
fstream f;
f.open ("information.dat",ios::app|ios::binary); //this is where I get the error.
...
}
The answers above address your initial problem. I'm going to talk about two more.
First, you probably should f.close() at the end of your method. It may be perfectly fine to let it drop out of scope and clean up from there, but I personally think that's ugly, and I wouldn't count on it.
Second, I wouldn't store the data in binary unless there's a really good reason to do it. It won't be portable. Serge above suggests a serialization method. I'd consider an alternate approach. I'd write to the file in a human readable form such as JSON. Yes, it's a little more work, but...
-If you change your class, your old files will still be readable
-They are portable across environments
-You can actually look at them and readily understand what they contain
So Serge's suggestions above aren't horrible, but I'd pick a more modern serialization / deserialization style.
Note that your f.write won't work because your object contains other objects, you don't know how they work under the hood. That string, for instance, almost certainly can't be dumped the way you're trying to do it. Plus you aren't only dumping your data.
Also, you should printf the sizeof(k). You might find it interesting information. Try to account for every byte. You could printf the sizeof(k.name) to help you work some of it out.
I'm almost positive the information doing so would surprise you, but I haven't actually done it myself, because I would never try to raw memory copy C++ objects, and that's in effect what you're trying to do.
I have a main function that prints a sudoku game to the console as I play. I call a print board method which outputs the board along with a 2D array with the numbers. I've been modifying the game to output to the file after every cout:
cout << "puzzle created" << endl;
output << "puzzle created" << endl;
Then the problem is that there is an error when I try the same with a method:
sudoku.printBoard(); //method to print board to console
I can't simply say:
output << sudoku.printBoard();
and I can't say:
output << "number";
in the method in the board.cpp file either.
Does anyone know any way around this?
This is the problem with print() member functions, and the reason you shouldn't write them. At least, not like this. You've locked yourself into printing to stdout, and now that you want to print somewhere else instead, you're stuck.
Instead, have a function that prints where you tell it to. This may be stdout or a file or whatever.
The function will be declared like this:
void printBoard(std::ostream& os);
and inside its definition, you will use os rather than std::cout.
Then your code will look like this:
sudoku.printBoard(std::cout);
sudoku.printBoard(output);
It works because both std::cout and output are of types that derive from std::ostream.
If you need the cout variant a lot, and you don't want to provide the argument every time because it's getting messy, simply provide an overload:
void printBoard()
{
printBoard(std::cout);
}
Now you can still write:
sudoku.printBoard();
Whether this is more or less confusing for developers on your project is for you to decide.
If you don't have other things to print, so that this is the only function of its kind within the type of sudoku, a more idiomatic approach would be to create an operator<< for that type:
std::ostream& operator<<(std::ostream& os, const SudokuBoard& board)
{
board.printBoard(os);
return os;
}
Now you can use it like this:
std::cout << sudoku;
output << sudoku;
Make a class that takes two ostreams in the constructor. One of these can be std::cout. Store these in the class as references.
Create output stream operators (<<) and use those to write to the two streams.
To use this in other classes pass it as a reference in their constructor (look up dependency injection).
Pass ofstream as a reference to your funtion or simply return string from your function and print it in main() or callee function.
In 2nd case you go like this
string f()
{
ostringstream os;
os<<"whatever I want to print in output";
....
...
return os.str();
}
int main() or //womain() from wherever you call
{
output<<f()<<endl;
}
For a program I'm writing for a project in my C++ class, one of the requirements is to use constructors to initialize data members in objects.
We also have to read from binary files.
The method I chose to accomplish this was:
// Loads invmast.dat or creates one if none exists
fstream invFile;
invFile.open("invmast.dat", std::fstream::in);
if (!invFile)
{
cout << "File invmast.dat not found, creating a new one." << endl;
invFile.open("invmast.dat", std::fstream::out | std::fstream::app | std::fstream::binary);
if (!invFile)
{
cerr << "Unable to create or open file invmast.dat; exiting." << endl;
exit (EXIT_FAILURE);
}
}
cout << "File invmast.dat opened successfully." << endl;
vector <InventoryItem> invMast;
//vector <InventoryItem>::iterator invMastIterator;
InventoryItem invLoader;
while ( invFile && !invFile.eof())
{
invFile.read(reinterpret_cast<char *>(&invLoader), sizeof(invLoader));
invMast.insert(invMast.begin(), invLoader);
}
I'd prefer to create a vector of objects and pass the arguments to the copy or default constructor, but I can't seem to find a way to do this.
Is there a way, or do I need to rethink my approach?
Thanks!
If you were simply constructing an element, you could use emplace_back to construct it directly in the vector:
invMast.emplace_back(some, constructor, parameters);
But here, since you’re initialising the InventoryItem from raw bytes, you probably just want to construct an object and move it into the vector:
invFile.read(reinterpret_cast<char *>(&invMast.back()), sizeof(invLoader));
invMast.push_back(std::move(invLoader));
Or default-construct an element and then fill it:
invMast.emplace_back();
invFile.read(reinterpret_cast<char *>(&invMast.back()), sizeof(InventoryItem));
I am trying to write pointers of a class into file and then reading it. Writing is just fine, but reading shows error of type conversion. Help please.
Take example of this(integer).. If we use int instead of int* then code executes but not fine.
#include<iostream>
#include<windows.h>
#include<fstream>
using namespace std;
void save(int *ptr)
{
ofstream data;
data.open("info.txt",ios::app);
if (data.is_open())
{
data<<ptr;
data.close();
}
else
{
cout << "Unable to open file";
}
}
int* loaddata()
{
ifstream data;
int ptr;
data.open("info.txt");
if (data.is_open())
{
while (!data.eof() )
{
data>>ptr;
}
data.close();
}
else
{
cout << "Unable to open file";
}
return ptr;
}
void main()
{
int a=0;
save(&a);
int *ptr=loaddata();
}
A pointer is just a memory address. You can write it just fine, as you said, but when you read it, it is still just a memory address. Unless the object that it was pointing to is at the exact same memory location when you read it, you will be "reading" a pointer to random data, which you cannot convert to the class of the object it was pointing to before.
It's like storing the location (lat/long) of a butterfly, then trying to find that butterfly just from that position. The butterfly is most likley in a completely different place now.
What you are trying is that was normally called serialization.
The idea is to write class instances ( all data contained ) and an ID which can be the address of the instance because this is a very well unique id. Your serialization library takes care that only one instance is written ( as only one data set is needed ) and all later writes of this instance are done only by writing the pointer.
Reading back is quite simple as well. You serialization library knows that it needs a instance of a class, generate a new one with the content as written before if not already done with the unique id ( maybe the pointer/address as mentioned before ). After that every try to get a "read pointer" results in setting the pointer the actual value of the new generated instance.
Have a look for serializer pattern or a concrete implementation like boost::serialize http://www.boost.org/doc/libs/1_58_0/libs/serialization/doc/index.html
I'm kind of confused by ofstream.
ofstream inherited from ostream. And it
also inherited method "operator<<" from
ostream.
ofstream x;
x << "hello world" << endl;
//cout << "hello world" << endl;
system("pause");
return 0;
The above code clip is trying to use an object of ofsream
to output "hello world" to the terminal just as cout did.
The above code clip can compile but shows nothing.
Why does it happen?
Thanks,
ofstream is an abstraction for a file object. In order to be able to create a file, you need to pass in the file's name. If you don't a default ofstream object is created (which is why it compiles). By itself, such an object isn't of much use. Try:
ofstream x( "out.txt" );
x << "hello world" << endl;
...
It's been a long time, but IIRC of stream is an output_file-stream which streams data into an opened file. For an ofstream object to actually print to the terminal you would have to make it open "/dev/console" or something similar. A plain instance of ofstream probably doesnt open /dev/console b/c you already have cout available.
http://en.wikipedia.org/wiki/Input/output_%28C%2B%2B%29
<iostream> contains the definition of basic_iostream class template,
which implements formatted input and output
<fstream> contains the definitions of basic_ifstream, basic_ofstream and
basic_fstream class templates which implement formatted input, output and input/output
on file streams.