Template for serialization of objects

68b0a5876289e2b5351cc1956ba80dc8
0
Almos 101 Feb 17, 2009 at 11:03

I was bored at work so I started playing with C++ binary file access and wrote a simple template for serializing structures to be written to binary file.
One obvious caveat - string fields must have fixed lengths(e.g. char name[80]). Written in NetBeans, compiled and tested with MinGW.

#include <string>
#include <fstream>
#include <vector>


using namespace std;

template<class C> class FileAccess {

private:
    string path;

public:
    FileAccess(string p){
        path = p;
    }

    void setPath(string p) {
        path = p;
    }

    bool write(C element) {

        fstream f (path.c_str(), ios::out | ios::binary);

        if(!f) return false;
        f.seekp(0, ios::end);
        f.write((char*) &element, sizeof(C));
        if(!f.good()) {
            f.close();
            return false;
        }
        else {
            f.close();
            return true;
        }
     }

    C read() {

        C buffer;
        fstream f(path.c_str(), ios::binary | ios::in);
        f.read((char*) &buffer, sizeof(C));
        return buffer;
    }

    bool serialize(vector<C> v) {
        int size = v.size();
        int i;
        fstream f(path.c_str(), ios::out | ios::binary | ios::app);
        if(!f) return false;

        f.seekg(ios::end);

        for(i=0; i<size; i++) {
            C record = v[i];
            f.write((char*) &record, sizeof(C));
        }
        
        if(!f.good()) {
            f.close();
            return false;
        }

        else {
            f.close();
            return true;
        }
    }

    vector<C> unserialize() {

        fstream f(path.c_str(), ios::in | ios::binary);
        vector<C> ret_vec;
        C record;

        while(f.read((char*) &record, sizeof(C))) {
            ret_vec.push_back(record);            
        }

        f.close();
        return ret_vec;
    }
    
    ~FileAccess() {
        
    }
};

4 Replies

Please log in or register to post a reply.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Feb 17, 2009 at 13:28

So, how is this going to work for FileAccess<std::string> for example? Also, why should the serializer be responsible for knowing the path to the file. Or rather, why should it always serialize to a *file* rather than a generic stream?

I would much rather have a class that just serializes to/from a generic stream, and on which I can define my own custom serialization for custom classes. And one which can serialize to/from multiple file formats (plain old binary, XML, INI files, …).

Maybe you should take a look at 3rd party solutions like Boost’s.

B7dcbc0c0f07253f25ff5c25fe38c081
0
SamuraiCrow 101 Feb 17, 2009 at 17:04

This routine is endian dependent: It assumes that the destination platform that is going to read the file is the same endianness as the one that wrote the file.

68b0a5876289e2b5351cc1956ba80dc8
0
Almos 101 Feb 18, 2009 at 13:01

Thank you for your comments. It was my first stab at serialization (actually, I wanted to check if I’ll be able to store data in binary file using C++) and so I’d like to ask two questions:

1.How to handle types that don’t have fixed size (such as strings)?
2.How to bypass the endianess problem?

Thanks in advance.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Feb 18, 2009 at 17:48

For strings and other arrays, a common strategy is to write the length as an integer, then the characters or elements of the array.

Another possible strategy specifically for strings is to use null termination, just like strings are stored in memory in C/C++. The caveat here is you won’t be able to have embedded nulls, but most of the time you won’t want them anyway.

For endianness, you just need to define the endianness of the file format, and ensure that you swap things around if you read/write it on a platform with the opposite endianness. If you only work on one platform, just define the file format endianness to match and forget about it.