-
October 20th, 2013, 12:51 PM
#16
Re: Save/load functions not working
Originally Posted by KruSuPhy
I was merely asking if there is a method similar to that pseudo-code,
How is a general method going to know what your object consists of? You still have to write the code to output the contents to a file, and write the code to read the file. There are no shortcuts.
The question really should be how you do this. In C++, you could overload the streaming operators (operator << for output and operator >> for input) for the class.
One thing I don't recommend is that "LoadGame()" pseudo-code you have. You shouldn't mix error handling with file reading logic in the same function. What if you want to read the file, and if it doesn't exist, output an error to a log file, or read from an alternate file, or any number of other things? The LoadGame() should just return an error or throw an exception if the file doesn't exist and not do anything else. Then the caller decides what to do instead of LoadGame() controlling what is done.
However did you do as D_Drmmr stated?
When you try to learn something new in C++, it's best to start with a small example to get familiar with the topic before using those techniques in an existing program. Just write a simple program that writes an int, a double and a string to a file and reads it back.
Regards,
Paul McKenzie
-
October 20th, 2013, 10:06 PM
#17
Re: Save/load functions not working
Code:
#include <iostream>
using namespace std;
class FileIO
{
public:
int numInt;
double numDouble;
string name;
};
int main()
{
FileIO file;
file.numInt = 1;
file.numDouble = 1.5;
file.name = "Bob";
ofstream saveFile("file.bin", ios::binary);
saveFile.write((char *)&file, sizeof(file));
saveFile.close();
FileIO file2;
ifstream loadFile("file.bin", ios::binary);
loadFile.read((char *)&file2, sizeof(file2));
cout << file2.numInt << endl << file2.numDouble << endl << file2.name << endl;
loadFile.close();
}
Here's the example I made. It works fine. I just don't understand why it doesn't work in my actual program, I believe I did everything the same. As far as I know. It might just be some newbie mistake, but I'm not so sure.
-
October 20th, 2013, 10:38 PM
#18
Re: Save/load functions not working
Originally Posted by KruSuPhy
Here's the example I made. It works fine.
It doesn't work fine.
Code:
#include <iostream>
using namespace std;
class FileIO
{
public:
int numInt;
double numDouble;
string name;
};
int main()
{
FileIO file;
file.numInt = 1;
file.numDouble = 1.5;
file.name = "Bob";
ofstream saveFile("file.bin", ios::binary);
saveFile.write((char *)&file, sizeof(file));
saveFile.close();
FileIO file2;
ifstream loadFile("file.bin", ios::binary);
loadFile.read((char *)&file2, sizeof(file2));
cout << file2.numInt << endl << file2.numDouble << endl << file2.name << endl;
loadFile.close();
}
You see what's in red? You cannot save or read from the file like this. I already mentioned this to you.
Your class has a std::string type, and that cannot be saved to a file or read from a file using this technique. That technique only works for POD types. It doesn't matter if it seems to work, it is wrong.
I wish C++ centric websites quit posting this as an example of saving a file. Too many new programmers use it as a template in saving files "in binary", and wind up in the same mess you're in now.
Again, write each item out to the file one item at a time.
Regards,
Paul McKenzie
Last edited by Paul McKenzie; October 21st, 2013 at 08:20 AM.
Reason: Changed non-POD to POD
-
October 20th, 2013, 11:02 PM
#19
Re: Save/load functions not working
To prove my point, try this program:
Code:
#include <iostream>
#include <string>
#include <fstream>
class FileIO
{
public:
int numInt;
double numDouble;
std::string name;
};
using namespace std;
int main()
{
FileIO file;
file.numInt = 1;
file.numDouble = 1.5;
for (int i = 0; i < 1000; ++i )
file.name += "Bob"; // create a very long name by concatenating Bob onto itself
ofstream saveFile("file.bin", ios::binary);
saveFile.write((char *)&file, sizeof(file));
saveFile.close();
FileIO file2;
ifstream loadFile("file.bin", ios::binary);
loadFile.read((char *)&file2, sizeof(file2));
cout << file2.numInt << endl << file2.numDouble << endl << file2.name << endl;
loadFile.close();
}
The code has made corrections to your previous posts (it was missing necessary #include files). But look what I have done -- I created a very long name just to show you this technique doesn't work.
What do you see now when you save and read the name back? I doubt you will see "Bob" repeated 1000 times.
I know you won't see the name, since sizeof(file) is the number of bytes that the class consists of, and you're using sizeof(file) to determine the number of bytes to read and write. The sizeof(file) will always be the same (probably less than 100), regardless of how many characters the std::string holds. So right there, there is no way for that code to work.
Regards,
Paul McKenzie
-
October 20th, 2013, 11:35 PM
#20
Re: Save/load functions not working
Okay, I apologize, and I'm not doubting what you're saying at all. However, could you explain how my example is wrong? I tested it and it worked well enough. I'm not sure if I understand what's wrong with it other than the really long name part. If that's it, couldn't it be fixed with a character limit? However, I will write each item one at a time, but couldn't that run into the same problem, or does writing them separately eliminate that problem?
-
October 21st, 2013, 02:32 AM
#21
Re: Save/load functions not working
Originally Posted by KruSuPhy
Okay, I apologize, and I'm not doubting what you're saying at all. However, could you explain how my example is wrong? I tested it and it worked well enough. I'm not sure if I understand what's wrong with it other than the really long name part. If that's it, couldn't it be fixed with a character limit? However, I will write each item one at a time, but couldn't that run into the same problem, or does writing them separately eliminate that problem?
Your code didn't work, it just seemed like it did. The reason for that is that the implementation of std::string can use what is known as a small string optimization. This means that small strings are stored in a fixed size buffer that is a member of std::string. When this happens and you store and load the bytes of the std::string instance to a file, it may happen to do what you wished. However, when you use a longer string (such that the small string optimization is not in effect, as Paul showed) or when you run your code on a different compiler, all of a sudden your program can crash.
That's the hard part of programming in C++. When you make a mistake and your program is broken, you don't know what will happen. It may crash, or it may appear to work fine. It may work on your machine, but not on another machine. So either you must learn to write C++ programs in a way that you can guarantee the code will always work (compiled with any C++ compiler, running on any PC), or you will end up with broken programs that mysteriously stop working at the worst possible time. That's why you cannot learn C++ by trial-and-error. You need to follow a descend method (i.e. book) that teaches you how to write correct C++ code.
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
October 21st, 2013, 04:50 AM
#22
Re: Save/load functions not working
Originally Posted by KruSuPhy
Okay, I apologize, and I'm not doubting what you're saying at all. However, could you explain how my example is wrong?
Unlike other computer languages, when you make a mistake in C++, you are not guaranteed anything will work correctly, as D_Drmmr stated.
I tested it and it worked well enough.
C++ doesn't work that way. That code could run on 1,000 machines, but fail on machine 1,001. It could be ok today, but as soon as you change compiler, compiler options, etc. the code no longer works.
As pointed out, the std::string class for your compiler more than likely uses a buffer for small strings, but that is an implementation detail. Nothing stops a compiler from implementing std::string to not using short string buffers. For example, earlier versions of Visual C++ do not use such a buffer, therefore your code would have failed even for short strings.
I'm not sure if I understand what's wrong with it other than the really long name part.
It is fundamentally wrong. Again, your class is a non-POD type due to it containing a std::string, and once it is of that type, you cannot use constructs such as your read and write code on such a type.
If that's it, couldn't it be fixed with a character limit?
No.
However, I will write each item one at a time, but couldn't that run into the same problem, or does writing them separately eliminate that problem?
Code:
class FileIO
{
public:
int numInt;
double numDouble;
std::string name;
};
//...
FileIO file;
//...
saveFile << file.name;
Do you understand the difference in doing this? The operator << is the "stream out" operator. It is overloaded to understand what a std::string is, and will extract the characters from the std::string and output those characters to a file. Your code doesn't do any of that -- it blindly gives write() a pointer and says "starting at this address, write sizeof(file) bytes". That doesn't tell write() anything about that special std::string type and how to get the data from this type.
That's why I mentioned in my previous post about that example showing writing binary to a file, and how I wished websites (and even books) quit showing that example, or at the very least, warn the new programmers when it cannot be used. Too many new programmers stumble across this "binary saving/reading" code, and think it's sound and can be used universally. Nothing can be further from the truth.
Regards,
Paul McKenzie
-
October 21st, 2013, 05:51 AM
#23
Re: Save/load functions not working
The problem is sizeof isn't always going to give you what you think it is. If your class contains a pointer for example, sizeof will tell you that pointer is 4 bytes long, without regard at all to the size of what it's pointing to. So when you go to save, your code won't save the correct amount of data, not will it follow to the pointer to save whatever that pointer's pointing to. You'll end up saving a number that means nothing when you read it back in, and not saving the data you were trying to save.
-
October 21st, 2013, 07:10 AM
#24
Re: Save/load functions not working
Originally Posted by Paul McKenzie
Your class has a std::string type, and that cannot be saved to a file or read from a file using this technique. That technique only works for non-POD types. It doesn't matter if it seems to work, it is wrong.
Only works for non-POD types??????
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
October 21st, 2013, 08:22 AM
#25
Re: Save/load functions not working
Originally Posted by 2kaud
Only works for non-POD types??????
I corrected the post to state POD types.
Regards,
Paul McKenzie
-
October 21st, 2013, 10:12 PM
#26
Re: Save/load functions not working
Okay, well, I finally understood what you all were telling me and went ahead and fixed it. It works/'seems' to work, so here's my code.
Code:
void Game::saveGame(Character player)
{
std::ofstream saveFile("savegame.bin", std::ios::binary);
saveFile << player.name() << "\n";
saveFile << player.hitPoints() << "\n";
saveFile << player.strength() << "\n";
saveFile << player.defense() << "\n";
saveFile << player.speed() << "\n";
saveFile << player.level() << "\n";
saveFile << player.gold() << "\n";
saveFile.close();
std::cout << "Game Saved!" << std::endl;
}
void Game::loadGame()
{
std::ifstream loadFile("savegame.bin", std::ios::binary);
Character player;
loadFile >> player.m_name;
loadFile >> player.m_hitPoints;
loadFile >> player.m_strength;
loadFile >> player.m_defense;
loadFile >> player.m_speed;
loadFile >> player.m_level;
loadFile >> player.m_gold;
startGame(player);
loadFile.close();
}
Is there any way I could improve this code, or is it as it should be?
-
October 21st, 2013, 10:36 PM
#27
Re: Save/load functions not working
What happens if the player name contains a space, or do your disallow such names?
For further exploration, you might want to consider if it makes sense to use purpose-built database to store the data persistently instead of coming up with a home brewed file format. It doesn't have to be a full blown external database as a database engine embedded into your program like SQLite could do well enough.
-
October 21st, 2013, 11:20 PM
#28
Re: Save/load functions not working
Well, I ran into that problem earlier. If the name contains a space, it only counts everything before the space. Which I assume you already knew. However, I'm not quite sure how to fix that. I'm sure it's probably something simple I just don't know how to do yet, and I'll look it up tomorrow night when I get home.
And thanks for the link to SQLite, I'll check it out tomorrow as well!
-
October 22nd, 2013, 06:27 AM
#29
Re: Save/load functions not working
Another possible way of storing the data would be as XML format. There are several free c++ XML libraries available.
All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!
C++23 Compiler: Microsoft VS2022 (17.6.5)
-
October 22nd, 2013, 10:00 PM
#30
Re: Save/load functions not working
I'm not quite sure how to implement the SQLite into my program, but i'm going to look it up and try.
Also, as for the name with spaces problem I'm having, I attempted to use getline() for the input, but it didn't work. It would just go crazy and send into an infinite loop for some reason..
Last edited by KruSuPhy; October 22nd, 2013 at 10:03 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|