10

Possible Duplicate:
How to serialize in c++?
How to implement serialization in C++

I've been toying around with C++ more and more these days and have only had a couple experiences with ofstream at this point. Most of said experiences have been doing simple file output of variables and reading them back in with ifstream. What I haven't done is anything with objects.

Let's assume that I have an object that is being written to frequently (say a game, and the object is the character) Every time the character is hit, the hp is re-written, every time they defeat an enemy they are gaining experience.... my basic idea is to write a simple text-based dungeon crawling game. But how am I going to make some kind of an autosave file?? Do I just write out every attribute of my object to a file individually and then move onto bigger and better from there? If I had to do it right now that's how I'd go about doing it, but I can't help like feeling that there's an easier way than that....

Anyone care to help me output the contents of an entire object(and it's respective attributes) to a file?

Community
  • 1
  • 1
HunderingThooves
  • 962
  • 8
  • 20
  • 33

6 Answers6

8

You could just write the object to a file by copying it's contents in memory.

But then you hit the tricky bits! You can't copy any items that are pointers to memory, because when you load them back in they don't own that memory. That means copying things like std::string which may internally contain their own memory allocation is also tricky.

Then even with standard types there are issues if you plan to read them on a different machine with a different number of bits, or a different byte order.

The process is called serialisation - there are a few standard techniques to make it easier.

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • 2
    You are not even allowed to `memcpy` `std::string`; any type that cannot legally be copied bit for-bit **in the same run of the program** cannot be written to a file/read back in another run of the program. But even writing/reading back an `int` ties the file format to the architecture and specific compiler options controlling the length of `short`, `int`, `long`, `double`... – curiousguy Oct 13 '11 at 06:37
4

Take a look at this code :

//! Struct for a 2d marker
struct Marker2d{
    double  x;                    //!< x coordinate of marker in calibration phantom
    double  y;                    //!< y coordinate of marker in calibration phantom
    int     id;                   //!< some unique id (used for sequence id as well as for assigned 3d id)
    int     code;                 //!< type of marker (small = 0, large = 1)
    float   size;                 //!< real size of marker in 2D image (in pixel)
    double  distanceToNearest;    //!< distance to nearest other marker

    /**
    *  Overloaded stream insertion operator. Abbreviation for the output of 2d marker.
    \param  output_out A reference to an std::ostream instance indicating the output stream
    \param  marker_in A constant Marker2d reference indicating the 2d marker that we want to output
    \return a std::ostream reference containing the new output data
    */
    friend std::ostream & operator<<(std::ostream & output_out, const Marker2d & marker_in)
    {
        return output_out<< std::fixed << std::setprecision(15) <<marker_in.x<<"\t"<<marker_in.y<<"\t"<<marker_in.id<<"\t"
        <<marker_in.code<<"\t"<<marker_in.size<<"\t"<<marker_in.distanceToNearest;
    }

    /**
    *  Overloaded stream extraction operator.
    \param  s_in A reference to an std::istream instance indicating the input stream
    \param  marker_out A Marker2d reference indicating the 2d marker that will have its data members populated
    \return a std::istream reference indicating the input stream
    */
    friend std::istream& operator>>(std::istream& s_in, Marker2d & marker_out)
    {
        s_in >> marker_out.x >> marker_out.y >> marker_out.id >> marker_out.code >> marker_out.size >> marker_out.distanceToNearest;
        return s_in;
    }
};

This is a simple struct with overloaded >> and << operators. This allows you to output to a file like myOfstreamFile << obj; And read the other way around.

If you have say, a thousand of objects stored in a file you can simply put them in a container like this :

std::vector<Marker2d> myMarkers;
std::ifstream f( fileName_in.c_str() );
if(!f)
   throw std::exception(std::string("AX.Algorithms.ComputeAssignmentsTest::getMarkersFromFile - Could not open file : " + fileName_in + " for reading!").c_str());

//cool one liner to read objects from file...
std::copy(std::istream_iterator<AX::Calibration::Marker2d>(f), std::istream_iterator<AX::Calibration::Marker2d>(), std::back_inserter(myMarkers));

Of course you could provide other forms of input and output e.g. save to .xml format and parse it as a dom tree etc. This is just a sample.

EDIT : This will work for relatively simple objects. Look at serialization if you need something more complex

FailedDev
  • 26,680
  • 9
  • 53
  • 73
  • 1
    This is a good start, but deceptive as soon as you reach more complex objects, like those containing arrays, strings, or pointers. – Mooing Duck Oct 12 '11 at 16:19
  • Also, I'm quite sure the definitions for the friend functions must be outside of the class, your code is incorrect. – Mooing Duck Oct 12 '11 at 16:20
  • @MooingDuck Actually the code is perfect and works as part of a really large project. So your downvote makes no sense at all. Also the OP wanted something simple so I gave a simple answer. – FailedDev Oct 12 '11 at 16:24
  • I stand corrected. I've always been told the insertion/extraction operators had to be outside of the class, and could not be inside. codepad says I was mistaken. I would retract the -1 for that alone, but I can't unless there's an edit. – Mooing Duck Oct 12 '11 at 16:38
  • @MooingDuck Edited. Just for you :D – FailedDev Oct 12 '11 at 16:46
  • @MooingDuck "_I'm quite sure the definitions for the friend functions must be outside of the class, your code is incorrect._" Just like any other function, they can be defined `inline` (implicitly, without the `inline` keyword). Also: some friend functions cannot even syntaxicaly be defined outline! – curiousguy Oct 13 '11 at 06:50
3

Search the web and SO for "serialization". There are some bumps to watch out for: floating point, endianess and variable length fields (strings).

Good luck!

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
1

Behind your simple question hides a complex theme. Please take a look to boost::serialization (here for instance). Any time spent to learning boost it's very rewarding.

CapelliC
  • 59,646
  • 5
  • 47
  • 90
1

There's a nice library in Boost called Boost.Serialize. If you're looking for performance, it is probably not the right choice, but looking at the source code and usage may give you some ideas. Serialization/Deserialization can be tricky, especially when you have lots of nested components. JSON is a very nice format for serializing general objects.

gred
  • 612
  • 1
  • 8
  • 15
1

Here's a hacky trick that will likely get coders here to rip out their hair and scream at me (this only works for static objects and not ones that use dynamic memory allocation):

class TestA
{
    private:
        long Numerics;
        char StaticArray[10];
        int Data[3];

    public:
        TestA(){Numerics = 10; strcpy(StaticArray,"Input data"); Data[0] = 100; Data[1] = 200; Data[2] = 300;}
        void Test(){Numerics = 1000; strcpy(StaticArray,"Data input"); Data[0] = 300; Data[1] = 200; Data[2] = 100;}

        void Print()
        {
            printf("Numerics is: %ld\nStaticArray is: %s\n%d %d %d\n",Numerics,StaticArray,Data[0],Data[1],Data[2]);
        }
};

int main()
{
    TestA Test;
    FILE *File = fopen("File.txt","wb");
    Test.Test();
    Test.Print();
    fwrite((char *)&Test,sizeof(Test),1,File); //Treats the object as if it is a char array
    fclose(File);

    TestA Test2;
    File = fopen("File.txt","rb");
    fread((char *)&Test2,sizeof(Test2),1,File); //Treats the object as if it is a char array
    Test2.Print();
    fclose(File);
    return 0;
}

Which results in:

Numerics is: 1000
Static array is: Data input
300 200 100
Numerics is: 1000
Static array is: Data input
300 200 100

Opening the file reveals the written data:
è Data input w, È d

The above trick allows for easy conversion into a byte-based format. Naturally this is hacky, but classes (or objects) should be expected to supply their own object-to-char array conversion process.

SE Does Not Like Dissent
  • 1,767
  • 3
  • 16
  • 36
  • Beware: sizeof("Input data") > sizeof(StaticArray). And Static... naming it's misleading here. I think the proper naming convention should be POD....If I had still some hair maybe I could rip off those now! – CapelliC Oct 13 '11 at 06:55