Hallo!

I've been having some trouble getting a save/load function to work properly. I'm trying to save object and their properties to a file in binary using fstream. And then being able to load the object from the binary file the next time im running my project.

Quick breakdown: I got this class CMapMgr which holds a vector of CMap instances, and each CMap instance holds a vector with CTile instances.

So when everything is initiated and running, and when i run this code, how much is saved?

// mFile is of fstream type and is a private member.

// mapList is the name of the CMapMgr's vector, with each element holding a CMap instance.

bool CMapMgr::save(std::string filename) {

    /** Load the file for binary output */
    mFile.open(filename.c_str(), std::ios::out | std::ios::binary);

    // Would it be useful to save length of the vector in it's current state? Or perhaps 
    // It's possible to tell the loading func the # of maps in any other way?

    int i=0;

    /** iterate through maps and save each one */
    for (std::vector<CMap>::iterator map = mapList.begin(); map!=mapList.end(); ++map) {

        if (mFile.write((char*)&map, sizeof(CMap))) { //Pass data to the file
            log_debug("MapMgr->save: saved %s as %i map", mapList[i].mMapName.c_str(), i);
            i++;
        }
        else {
            log_error("MapMgr->: failed to save %s", mapList[i].mMapName.c_str());
            return false;
        }
    }
    
    mFile.close();
    return true;
}

**I might doing something wrong in my iteration too, dont have all that much knowledge about iterations.

Question 1: So, when I first saves this and then trying to read from it. Do I need to save the length of the vector to be able to iterate through and load later. Or is it possible to try read a map object from the file and on fail, stop loading maps? Or could it result in a "faulty object"? Now when i try to load i use about the same way as saving, only reading instead of writing. Should do the trick?

Question 2: When i write this to the file, does it mean the instances of an instance gets written to file? As each CMapMgr vector index holds a instance of CMap which should hold seveal CTiles instances.


Would greatly appreciate some hints on this one! Sorry for my lack of terminology & probably grammar aswell. And I hope my questions arent too diffuse. I'm not after code hints (well maybe some), but more a explanation of how it works to write/read objects from file.

Thanks ~ R

Edited 6 Years Ago by Rikard: n/a

Q1: Hmmm.... Something like this has alot of overlapping constructors and function calls, it's an exercise in using resources efficiency. I'm actually working through a similar situation on a project I'm working on right now.

This is how I'm approaching it:
To prevent multiple re-allocations of the vector as it grows, I think I would save the vector's current capacity in the file. After you read that number, call vector::reserve() to pre-allocate the appropriate amount of space then continue with the rest of your load function, using vector::push_back() to add the new objects to the vector. The problem is, if you construct the object before you push it, you wind up constructing it, updating it, then constructing a copy (constructing it twice, uses 3 functions, and more memory). Another option is to push a default object, then update it with it's own read function (only uses 2 functions, and less memory). You would then pass a reference to the existing file stream to the pushed object's own read method.

Q2: It depends on how you set it up, but essentially yes. Your file would have a format similar to this:

CMapMgr vector capacity
  CMap 0 information
    CTile 0-0 information
    ...
    CTile 0-n information
  CMap 1 information
    CTile 1-0 information
    ...
    CTile 1-n information
  ...
  CMap x information
    CTile x-0 information
    ...
    CTile x-n information

Hopefully that helps at least a little...

Edited 6 Years Ago by Fbody: n/a

Hello, thanks for you response! Yea it helped, the main thing i was curious about were how much of the "derived object + properties" got saved while saving the "top-instance" of a serie of instances. (got a hard time describing this) But if i got you right, everything gets written to my save file. Atleast with the way I've implemented CMap & CTile it seems reasonable that everything gets added to the stream before writing.

I gotta say i like your suggestion about a default object which is updated with fetched data from the savefile.

Just thought about something, would'nt it be possible to save the entire CMapMgr vector at once? No iteration and such, only to make a "blueprint" of the entire vector at once... sort of. And with that later loading the entire vector at once, still as a whole, no iteration or such (well not until updating the properties on each object). If that would be possible only problem would be knowing the what length of data to fetch on load (as its a vector and therefor dynamical length). But i guess the size information could be saved aswell.

Would something like this work? And later "restore" the mapList using mFile.read(same arg). And as mentioned, if it works I'd probably be needing to save the mapList.size() first so mFile.read know how much it should read?

if (!mFile.write((char*)&mapList, sizeof(mapList))) {
        log_error("CMapMgr->save: unable to write mapList to file...");
        return false;
    }

If this would work, saving the length/size info of the vector before the code above, could be achieved doing this??

mFile.write((char*)&mapList.size(), sizeof(int));

Taking this code out of thin air... I see this doesent work but hopefully im approaching something concrete. :-)
You should note that I hav'nt worked a whole lot with i/o/f -streams before. Even though I've been @ programming for some time I still hav'nt got a hang of this... so I'm considering myself quite the beginner at this. So a pedagogical answer is preferred :-)

Edited 6 Years Ago by Rikard: n/a

>>how much of the "derived object + properties" got saved while saving (well almost) the "top-instance" of a serie of instances
That is entirely dependent on how you write your function(s).

>>would'nt it be possible to save the entire CMapMgr vector at once?
I'm definitely not the most advanced programmer, so it may be possible. But I doubt it. You could certainly write a function to do so, but then you get back into the topic(s) previously discussed. Whether the posted code works well or not would be highly dependent on how sizeof() views the mapList. I doubt it would see enough of it...

Edited 6 Years Ago by Fbody: n/a

I'm sorry to say this, but there is something awfully wrong with your implementation. If you cast a pointer to CMap to a char pointer and save, in binary, sizeof(CMap) bytes, you will also save other "undesirable" things, especially the virtual table pointer (vptr) which is not going to be valid the next time you load it. If it happens to work in your save and load tests, it is just by sheer luck. Similarly, for the vector of CTiles inside the CMap object, when it is saved by just a direct pointer cast, it will probably save its internal data, like size, capacity, and a pointer to the internal array. That pointer to the internal array will be meaningless when you load it again and you will have a big memory corruption problem.

To implement a serialization library correctly, I would first recommend you look at how the boost serialization library does it. It is a very nice library and I used this approach on my own project and it is very robust.

The basic idea is that you make a class, similar in interface to iostreams in C++ std libraries, that encapsulate the format with which data is saved (this allows you to seamlessly save/load from binary or XML or any order format without change any code (just by using a different class derived of your basic stream classes). You can also use the << and >> operators for convenience. You just need to write a bunch of overloads for each primitive types (float, double, int, uint, bool, etc.) and one overload for a "serializable" object (serializable being the basic interface class for the pure virtual save/load functions). Then, each class you create, that you want to be serializable, has to derive from interface "serializable" and implement a save/load function. It sound cumbersome at first but it gets very powerful, very quickly. With an additional trick, you can also serialize arbitrarily complex object graphs. This is what I do in my project, I have one application that builds the software (say a control system for a robot) as a complex object graph (with cycles and trees) and serializes it to a file (in XML or binary, doesn't matter). Then, in the actual application, the user can just select the control system he wants to run by loading the program (in the form of an object graph) from the file(s). The same can be done for a computer game, you can save the entire game (not only the data like textures and maps, but also the game logic, the controllers, the bots, etc.) from serialized object graphs in files (load and unload when going from one level to another also). This is awesome to work with, so I suggest you take a good look at the boost serialization library, and use it if it is appropriate for your application, but writing a tailor-made one on your own is quite easy too.

Hey, no problem at all. I'm grateful for your honesty and answer. My code example was just something I wrote in haste too. But I guess that in the far end I'm not rly sure on what im doing here. I hadn't even thought about all the extra data that gets written in my example. It actually compiles fine. However I hadn't really started on a loading function so never got the change to try if it worked. Thought i'd get saving done before.

Saving in binary might be overkill for this as i could aswell save everyhing in text format. (which is what i did before trying this)

3 things makes me wanna save in binary format though.

# It's not readable for humans and therefor difficult to alter.
# I'm developing this game (well game-editor really) just as much for the sake of learning as for the final product / my goal.
# Once i've understood more of how serialization works I had in mind to build a serialization class which could read/write anything thrown at it. And I enjoyed to read that part when you mentioned it's possible to save just about anything in binary. Was something like that I wanted to achieve. Maybe not saving the entire application itself, but never the less, anything related too being "in-game".

Felt like a fitting part to try saving the world->maps->tiles first.
I'll have a look at the boost serialization library and read up some. Thanks a bunch!

PS. I wanna add that your work-title sounds quite awesome Mike! ;)

This article has been dead for over six months. Start a new discussion instead.