Save/Load routine failing. some help?

Started by
16 comments, last by Radagar 21 years, 7 months ago
I have a CPlayer class created and I'm trying to write it to a file and read it back using fwrite and fread. If I write to file, then read it back in the same run, it works perfectly. If I write to file. Close, then just run the code to read the file, some of the variables don't read right. Here is the code...
      
//My CPlayer Class

class CPlayer
{
public:
//Constructor & Destructor

	CPlayer();
	~CPlayer();
//General Variables

	string	name;	//Players Name

	string	race;	//Players Race

	string	Class;	//Player Class

	int		gold;
	int		exp;
	int		level;
//Stat Variables

	int		hp;		//Current hitpoints

	int		maxhp;	//Max hitpoints

	int		mp;		//Current magic points

	int		maxmp;	//Max magic points

	int		STR;	//Players Strength

	int		DEX;	//Players Dexterity

	int		INT;	//Players Intellegence

	int		CON;	//Players Constitution

	int		WIS;	//Players Wisdom  

	int		CHA;	//Players Charisma

	int		hitdie;	//Players Hit Point Die


//Class Functions

	void	Create(bool isnpc, CPlayer* npc);				//Character Creation

	void	LevelUp();				//Character Gains a Level

	void	Move(int direction);	//Character Moves on Combat Map

	void	View();
};

////////////////////////////////////

////////////////////////////////////


//My main() Function

/////////////////////

int main()
{ //First part of Code

	srand((unsigned) time(NULL));
	CPlayer Player0;
	CParty Party;
	Party.player[0] = &Player0
	Party.player[0]->Create(0, NULL);
	Party.player[0]->View();
	save(Party.player[0]);
	//Second Part of Code

	CPlayer PlayerLoaded;
	CPlayer* pPlayerLoaded = &PlayerLoaded
	load(pPlayerLoaded);
	cout<<"\n\nname = "<<PlayerLoaded.name<<endl;

	return 0;
}
  
If I run my code like this, it will write 'Party.player[0]' to file, then it will read it back in to pPlayerLoaded. That works fine. However, if I run just the first part of the code (Up to just past the Save routine), then re-run with just the Load code, some members of the CPlayer class, mainly the name, race, and class, are not reading back in... when it goes to display the name member, it's just gibberish. is this a problem with std::string not reading back in right? the int's seem fine. Following are the save/load routines.
        
//SAVE ROUTINE

////////////////

void save(CPlayer* theplayer)
{
	FILE *savefile;

	if( (savefile = fopen( "testfile.txt", "wb" )) != NULL)
	{
		//File Opened or Created

		fwrite(theplayer, sizeof(CPlayer), 1, savefile);
		cout<<"\nDone Saving\n";
		fclose(savefile);
	}
	else
	{
		cout<<"Couldn't open savefile"<<endl;
	}
}
//////////////


//LOAD ROUTINE

//////////////////

void load(CPlayer* theplayer)
{
	FILE *savefile;

	if( (savefile = fopen( "testfile.txt", "rb" )) !=NULL)
	{
		fread(theplayer, sizeof(CPlayer), 1, savefile);
		cout<<"\nDone Loading\n";
		fclose(savefile);
	}
	else
	{
		cout<<"Couldn't open file for loading"<<endl;
	}
}
      
Any suggestions? ~~~~~~~~~~~ Chris Vogel ~~~~~~~~~~~ --edit typos [edited by - Radagar on September 10, 2002 11:07:22 PM] [edited by - Radagar on September 10, 2002 11:07:53 PM]
WyrmSlayer RPG - In Early Development
Advertisement
I don't know, but I think that the problem is that the strings are kinds arrays, so when you tell the computer to write the class, it will write a pointer to the array of chars in the file, instead of writing the array itself. I guess you will have to manually save each string. I don't know very well how to do that, but I think there's a funtion similar to sizeof() that will work on strings (don't use sizeof() here, it will only work with constants arrays, with is not the case of string). Try somehting like:


      fwrite(theplayer, sizeof(CPlayer), 1, savefile);fwrite(theplayer.name, GetStringSize(name), 1, savefile);fwrite(theplayer.class, GetStringSize(class), 1, savefile);fwrite(theplayer.race, GetStringSize(race), 1, savefile);cout<<"\nDone Saving\n";fclose(savefile);    


However, I never really used strings, so I don't know if they are closer to be something like a class, or something closer to a array of chars.


[edited by - algumacoisaqualquer on September 11, 2002 2:18:10 AM]
you could save yourself alot of trouble and avoid having to
update your code later if you switched to a c++ standard method
for file i/o...

use fstream!!



-eldee
;another space monkey;
[ Forced Evolution Studios ]

::evolve::

-eldee;another space monkey;[ Forced Evolution Studios ]
i know that std::string has problems with C file reads and writes.
every post i see says append .c_str() to the string (i believe).
hopefully that helps

Knowledge is what you learn, wisdom is how you apply it.






find your element
at mutedfaith.com.
<º>

Beginner in Game Development?  Read here. And read here.

 

quote:Original post by Alpha_ProgDes
i know that std::string has problems with C file reads and writes.
every post i see says append .c_str() to the string (i believe).
hopefully that helps

well, from what i gather he''s not passing a string to
his save/load function.. and if he did, he''d get a compiler
error since fread/fwrite takes a const char*



-eldee
;another space monkey;
[ Forced Evolution Studios ]

::evolve::

-eldee;another space monkey;[ Forced Evolution Studios ]
man, the help tonight ... you see that he''s outputing a fixed size, and reading back in a fixed size ... isn''t that a clue ...

here''s the deal ... you are using simple binary writing and reading functions, which work just like memcpy, but to a file ... right ...

BUT YOUR STRUCTURE HAS POINTERS IN IT ... and pointers cannot be stored out to a file and used again later ...they don''t mean anything later ... and the actual data pointed to won''t be there ...

think about it ... if you had a class like this:

struct BinarySafe {
int x;
int y;
char name[30];
};

then you can see that it works just fine .. cause it would be 38 bytes long, including all the data, and nothing but data ...

but if you had this:

struct UsesPointers {
int x;
int y;
char *name;
};

then you would be writing and reading 12 bytes ... the last 4 of which are just a pointer .. and NONE of which is a name.

this same thing would be true of any situation using pointers, and should be assumed to be true of any situation using classes ... (because you cannot copy classes into each other binary, you MUST use constructors) ....

the ANSWER is that you must go a level deeper to output each of your poitner or class objects ... for example, to read and write the UsesPointers struct above , you might do this:


  void write_c_str(FILE *file, char *str)  {  short len = strlen(str); // get the string length  fwrite(&len, sizeof(len), 1, file); // write the string length  fwrite(str, len, 1, file); // write the string  }void read_c_str(FILE *file, char **str)  {  short len;  fread(&len, sizeof(len), 1, file); // read the string length  delete [] *str; // release any old memory in str  *str = new char[len]; // allocate enough space for new string  fread(*str, len, 1, file); // output the string  }void save(UsesPointers *up)  {  fwrite(up->x, sizeof(up->x), 1, file);  fwrite(up->y, sizeof(up->y), 1, file);  write_c_str(file, up->name);  }void load(UsesPointers **up)  {  fread(up->x, sizeof(up->x), 1, file);  fread(up->y, sizeof(up->y), 1, file);  read_c_str(file, &(up->name));  }  


Good Luck ... and these are just samples ... but I hope you see what I''m talking about now ...
You should not ever try to convert instances of classes to a simple void* pointer, that will break the encapsulation of the class at minimum. It could also have other sideeffects, like the ones you are experiencing.
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
Thanks everyone. I''ll try writing and reading each member of the class instead of the actual class. I see how having pointers in teh class would screw things up, since the pointers don''t mean anything later. Explains why it DOES work when I save and load on the same run too.



~~~~~~~~~~~
Chris Vogel
~~~~~~~~~~~
WyrmSlayer RPG - In Early Development
quote:Original post by eldee
you could save yourself alot of trouble and avoid having to
update your code later if you switched to a c++ standard method
for file i/o...

use fstream!!

-eldee
;another space monkey;
[ Forced Evolution Studios ]

::evolve::




Would using FSTREAM make it any easier to write out a full class with pointers, without writing each member of that class seperately? I''ve not used FSTREAM, and I''m not familiar with it''s syntax.



~~~~~~~~~~~
Chris Vogel
~~~~~~~~~~~
WyrmSlayer RPG - In Early Development
Radagar:

Using strings with fstream is fine. If you use the string class, they are not treated as arrays, but as a data type. Here's an example I whipped up that shows using fstream with a class that contains a string. First, the header:


      //test.h#include <string>using namespace std;class CTest {        string test_name;        int test_num;        public:        string get_test_name() { return test_name;};        int get_test_num() {return test_num;};        void set_test_name(string name) {test_name = name;};        void set_test_num(int num) {test_num = num;};        CTest() {  };        ~CTest() {  };};    

and the test.cpp file:


        //test.cpp#include <iostream>#include <fstream>#include <string>#include "test.h"using namespace std;int main();                     //prototypeint main() {        CTest test1, test2, test3, test4;        char file[] = "testfile";        char *file_ptr;        string name1("name1");        string name2("name2");        string name3, name4;        int three, four;        file_ptr = file;        test1.set_test_name(name1);        test1.set_test_num(1);        test2.set_test_name(name2);        test2.set_test_num(2);        //open the file to write        ofstream outfile(file_ptr, ios::out | ios::binary);        if (!outfile) {                //couldn't open file                cout << "couldn't open file" << endl;        }        outfile.write((char *) &test1, sizeof(CTest));        outfile.write((char *) &test2, sizeof(CTest));        outfile.close();        //open the file to read        ifstream infile(file_ptr, ios::in | ios::binary);        if (!infile) {                //couldn't open file                cout << "couldn't open file" << endl;        }        infile.read((char *) &test3, sizeof(CTest));        infile.read((char *) &test4, sizeof(CTest));        infile.close();        name3 = test3.get_test_name();        three = test3.get_test_num();        name4 = test4.get_test_name();        four = test4.get_test_num();        cout << "test1 and test3's name is: " << name3 << endl;        cout << "test1 and test3's number is: " << three << endl;        cout << "test2 and test4's name is: " << name4 << endl;        cout << "test2 and test4's number is: " << four << endl;}   


I got the expected output, which was:

test1 and test3's name is: name1
test1 and test3's number is: 1
test2 and test4's name is: name2
test2 and test4's number is: 2

I would suggest that you use the C++ stream I/O classes rather than the old C I/O functions.

John.

[edited by - JohnAD on September 11, 2002 5:32:27 PM]

[edited by - JohnAD on September 11, 2002 5:33:44 PM]

This topic is closed to new replies.

Advertisement