saving info in a class to a file

Started by
9 comments, last by Scythen 19 years, 2 months ago
Whats the best way to save all the attributes in a class to a file, do you have to just save them one by one, or can you save the whole class as binary data. Thanks for the input
Advertisement
You can save each property one by one but it would probably be easier (and more flexible) to use a serializaiton technique. When you serialize an object (or objects), you "flatten" them into a stream of bytes which may then be saved to a file or sent out across a network. The application opening the file (or recieving the packet) must know the format of the data so that it can deserialize it. Read Enginuity V for a better idea of how this works.
Quit screwin' around! - Brock Samson
If the class is a POD-type that does not contain pointers, you can save it as binary data. Otherwise, you need to save member by member. If the class contain pointers, you also need to decide on what you're going to do with them.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
C++ doesn't have serialization as java does, so, you have to do it yourself.

If you have a simple class with basic types, you can write it all to a file in one chunk, but if you have any more complex types like.. std::string etc etc, you need a save-function that saves them one by one, and loads them the "same" way...

Good luck!
If you're working in Nemerle (which I highly doubt you are), you might find this useful: http://nemerle.org/macros.html#attributes.


"There is no dark side of the moon really,
As a matter of fact, its all dark."
Be carefull with doing a load/save on the & of a class - you
might just overide the vtable if it's a class with a virtual
functions (I bet that would give 'interesting' effects :)

Concerning pointers - try to avoid them in structures you
wanna write to disk (it only gives a headache)...
Instead use id's referencing the other data and a manager
which will give you the pointer when passed this ID.


Maybe you could do sth like this:

class Blah
{
// optional: pack struct to 1 byte-boundaries
// We use a struct so we don't have to save every single
// variable seperately
struct
{
int a
char b;
} data;

void write (...)
{ fwrite (&data, sizeof(blah), 1, fp);
}

void read (...) { ... }
}

Note that if you are writing lots of different (derived) classes, you'll
have to write sth in advance (eg 1 byte to specify the class), so you know
which class to create beforehand (you have to have an instance of the correct
class, before you can call it's read function!)

Now that i'm thinking about it - maybe load/save functions shouldn't
be class-members, but rather there should be another class which is
able to save a class passed in as a parameter.... hmmm...

Anyway, this is just an idea on how you could do it -
I'm sure there are better solutions out there ;)

/R
visit my website at www.kalmiya.com
fprintf
Here is a little example of saving the entire structure into a file, then reloading it.

// Pointless C styled POD Version utilitizing modern C++ code// Link from Fruny: http://www.parashift.com/c++-faq-lite/intrinsic-types.html#faq-26.7#include <string>#include <fstream>#include <iostream>#include <sstream>using namespace std;struct tMyStruct{	int Age;	char Gender;	bool Happy;	char MyName[64];	tMyStruct()	{		Age = 1;		Gender = '?';		Happy = 0;			memset(MyName,' ',64);		MyName[63] = 0;	// null terminate it	}};void Save(){	cout << "Saving tMyStruct to file..." << endl << endl;	tMyStruct st1;	std::stringstream sstr,sstr2;	sstr.clear();	cout << "These are the default values of tMyStruct" << endl << endl;	sstr << "Age: " << st1.Age << endl 		<< "Gender: " << st1.Gender << endl		<< "Happy: " << st1.Happy << endl		<< "MyName: " << st1.MyName << endl;	// Output contents	cout << sstr.str();	st1.Age = 19;	st1.MyName[0] = 'D';	st1.MyName[1] = 'r';	st1.MyName[2] = 'e';	st1.MyName[3] = 'w';	st1.MyName[4] = '_';	st1.MyName[5] = 'B';	st1.MyName[6] = 'e';	st1.MyName[7] = 'n';	st1.MyName[8] = 't';	st1.MyName[9] = 'o';	st1.MyName[10] = 'n';		st1.Happy = 1;	st1.Gender = 'M';	cout << "These are the new values of tMyStruct" << endl << endl;	sstr2.clear();	sstr2 << "Age: " << st1.Age << endl 		<< "Gender: " << st1.Gender << endl		<< "Happy: " << st1.Happy << endl		<< "MyName: " << st1.MyName << endl;	// Output contents	cout << sstr2.str() << endl;	// Now tell the size	int size = sizeof( tMyStruct );	cout << "Size of tMyStruct: " << size << endl << endl;	// Get start location of struct	void* tPtr = (void *)&st1;	// Buffer for the data	char* buffer = 0;	buffer = new char[size];	// Copy of the struct	memcpy( buffer, tPtr, size);	ofstream OF;	OF.open("test.txt");	OF.write( (char*)buffer, size);	OF.close();	delete [] buffer;	cout << "tMyStruct successfully saved to file!" << endl << endl;}void Load(){	cout << "Loading tMyStruct to file..." << endl << endl;	tMyStruct st1;	std::stringstream sstr,sstr2;	sstr.clear();	cout << "These are the default values of tMyStruct" << endl << endl;	sstr << "Age: " << st1.Age << endl 		<< "Gender: " << st1.Gender << endl		<< "Happy: " << st1.Happy << endl		<< "MyName: " << st1.MyName << endl;	// Output contents	cout << sstr.str() << endl;	// Now tell the size	int size = sizeof( tMyStruct );	cout << "Size of tMyStruct: " << size << endl;	// Get start location of struct	void *tPtr = (void *)&st1;	// Buffer for the data	char *buffer = 0;	buffer = new char[size];	// Now read in the data	ifstream IF;	IF.open("test.txt");	IF.read( buffer, size );	IF.close();	// Copy of the struct	memcpy( tPtr, buffer, size);	sstr2.clear();	cout << "These are the new values of tMyStruct" << endl << endl;	// Show the new data	sstr2 << "Age: " << st1.Age << endl 		<< "Gender: " << st1.Gender << endl		<< "Happy: " << st1.Happy << endl		<< "MyName: " << st1.MyName << endl;	// Output contents	cout << sstr2.str() << endl << endl;	delete [] buffer;	cout << "tMyStruct loaded from file!" << endl;}int main(int argc, char* argv[]){	Save();		Load();	return 0;}


This is just a trivial example, but it demonstrates how you would go about doing this. Make sure you heed everyone's advice about pointers! Notice what I had to do for the MyName variable - I made it into an array so the contents can be written out. If your class has pointers to other objects, you would have to setup a system so those objects are loaded from files first, then the addressed are assigned into the current class. I'd use Kitt3n's suggestion and use an ID.

Hopefully you should be able to see that there's not much to it really. My example only saves it to a text file. You could take it one step further and save it as a binary file as well. I normally don't use all of that C++ code, but since it seems like everyone is using it nowadays, I converted it. At first it was littered with printf's and sprintf's [smile]. Using the new C++ techniques will make your life a lot easier! Take that into consideration with your design model.

Hope this helps some.

- Drew

[edit] Added the top two comments [smile] [/edit]

[Edited by - Drew_Benton on January 31, 2005 5:31:11 PM]
Quote:
// Copy of the structmemcpy( buffer, tPtr, size);


[attention][attention][attention][attention][attention]

Quote:Make sure you heed everyone's advice about pointers!


It's not just "pointers". The internal layout of a non-POD type is not yours to manipulate. Your code will only work with C-compatible structs.

You were already writing each member to a stringstream... Why did you give up and used a memcpy()???
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
You were already writing each member to a stringstream... Why did you give up and used a memcpy()???


At the time it seemed like a good idea [lol]. Actually I was thinking about that question myself, then I stumbled upon this thread. I originally had a lot of C-styled stuff, but then I'd figured I'd be getting lectures of how I should be using C++ styled code, so I made the conversion. I took a look at your link and read a bit about the POD type, but I now realize I totally misinterpreted it.

But hey, two ways for the price of one (I think)! I guess remove the memcpy for C++ Non-POD classes and then use it when you have C-styled POD structs [wink]. Thanks for that info!

- Drew

[edit] Here is the correct C-styled version [/edit]
// C styled POD Version// Link from Fruny: http://www.parashift.com/c++-faq-lite/intrinsic-types.html#faq-26.7#include <string.h>#include <fstream.h>#include <stdio.h>struct tMyStruct{	int Age;	char Gender;	bool Happy;	char MyName[64];	tMyStruct()	{		Age = 1;		Gender = '?';		Happy = 0;			memset(MyName,' ',64);		MyName[63] = 0;	// null terminate it	}};void Save(){	printf( "Saving tMyStruct to file...\n\n");	tMyStruct st1;	char sstr[1024],sstr2[1024];	memset( sstr,0,1024);	memset( sstr2,0,1024);	printf("These are the default values of tMyStruct\n\n");	sprintf(sstr, "Age: %i\nGender: %c\nHappy: %i\nMyName: %s\n\n", st1.Age, st1.Gender, st1.Happy, st1.MyName);	// Output contents	printf( sstr );	printf( "\n\n");	st1.Age = 19;	st1.MyName[0] = 'D';	st1.MyName[1] = 'r';	st1.MyName[2] = 'e';	st1.MyName[3] = 'w';	st1.MyName[4] = '_';	st1.MyName[5] = 'B';	st1.MyName[6] = 'e';	st1.MyName[7] = 'n';	st1.MyName[8] = 't';	st1.MyName[9] = 'o';	st1.MyName[10] = 'n';		st1.Happy = 1;	st1.Gender = 'M';	printf( "These are the new values of tMyStruct\n\n");	sprintf(sstr2, "Age: %i\nGender: %c\nHappy: %i\nMyName: %s\n\n", st1.Age, st1.Gender, st1.Happy, st1.MyName);	// Output contents	printf( sstr2);	printf( "\n\n");	// Now tell the size	int size = sizeof( tMyStruct );	printf( "Size of tMyStruct: %i\n\n", size);	// Get start location of struct	void* tPtr = (void *)&st1;	// Buffer for the data	char* buffer = 0;	buffer = new char[size];	// Copy of the struct	memcpy( buffer, tPtr, size);	ofstream OF;	OF.open("test.txt");	OF.write( (char*)buffer, size);	OF.close();	delete [] buffer;	printf( "tMyStruct successfully saved to file!\n\n");}void Load(){	printf( "Loading tMyStruct to file...\n\n");	tMyStruct st1;	char sstr[1024],sstr2[1024];	memset( sstr,0,1024);	memset( sstr2,0,1024);		printf( "These are the default values of tMyStruct\n\n");	sprintf(sstr, "Age: %i\nGender: %c\nHappy: %i\nMyName: %s\n\n", st1.Age, st1.Gender, st1.Happy, st1.MyName);	// Output contents	printf( sstr );	printf( "\n\n");	// Now tell the size	int size = sizeof( tMyStruct );	printf( "Size of tMyStruct: %i\n\n", size);	// Get start location of struct	void *tPtr = (void *)&st1;	// Buffer for the data	char *buffer = 0;	buffer = new char[size];	// Now read in the data	ifstream IF;	IF.open("test.txt");	IF.read( buffer, size );	IF.close();	// Copy of the struct	memcpy( tPtr, buffer, size);	printf( "These are the new values of tMyStruct\n\n" );	// Show the new data	sprintf(sstr2, "Age: %i\nGender: %c\nHappy: %i\nMyName: %s\n\n", st1.Age, st1.Gender, st1.Happy, st1.MyName);	// Output contents	printf( sstr2 );	printf( "\n\n");	delete [] buffer;	printf( "tMyStruct loaded from file!\n");}int main(int argc, char* argv[]){	Save();		Load();	return 0;}


Here is the output:
Saving tMyStruct to file...These are the default values of tMyStructAge: 1Gender: ?Happy: 0MyName:These are the new values of tMyStructAge: 19Gender: MHappy: 1MyName: Drew_BentonSize of tMyStruct: 72tMyStruct successfully saved to file!Loading tMyStruct to file...These are the default values of tMyStructAge: 1Gender: ?Happy: 0MyName:Size of tMyStruct: 72These are the new values of tMyStructAge: 19Gender: MHappy: 1MyName: Drew_BentontMyStruct loaded from file!Press any key to continue


[Edited by - Drew_Benton on January 31, 2005 6:46:05 PM]

This topic is closed to new replies.

Advertisement