I currently use this to serialize data, write it to shared memory so that another program can reconstruct it and use it:

template<typename T>
void Serialize(unsigned char* &Destination, const T &Source)
{
    CopyMemory(Destination, &Source, sizeof(T));
    //Destination += sizeof(T);  //Not sure if to add this or not.
}

template<typename T>
void Serialize(unsigned char* &Destination, const std::vector<T> &VectorContainer)
{
    size_t Size = VectorContainer.size();
    Serialize(Destination, Size);
    for (size_t I = 0; I < Size; ++I)
        Serialize(Destination, VectorContainer[I]);
}

template<typename T>
void DeSerialize(unsigned char* &Destination, const T& Source, size_t Size)
{
    CopyMemory(Destination, &Source, Size);
}

template<typename T>
void DeSerialize(std::vector<T>* &VectorContainer, const T &Source, size_t ContainerSize, size_t ByteSize)
{
    VectorContainer.resize(ContainerSize);
    CopyMemory(VectorContainer, &Source, ByteSize);
}

I'm not sure if my DeSerialize is correct though. The data I want to deserialize looks like: http://www.daniweb.com/software-development/cpp/threads/430518/shared-memory-objects-across-dlls

How should I Deserialize it?

I tried:

DeSerialize(ListOfFonts, Data[7], sizeof(FontChar));
return &ListOfFonts[0];  //Return a void* to my vector containing the reconstructed data.

Recommended Answers

All 5 Replies

I've added this:

template<>
void Serialize<Model>(unsigned char* &Destination, const Model &M)
{
    Serialize(Destination, M.SX);
    Serialize(Destination, M.SY);
    Serialize(Destination, M.Stride);
    Serialize(Destination, M.ID);
    Serialize(Destination, M.TriangleCount);
    Serialize(Destination, M.Vertices);
}


void* GetData()
{
    unsigned char* Data = reinterpret_cast<unsigned char*>(LD);     //The long double that holds the map memory.
    ListOfData.resize(LD[2]);                                       //LD[2] holds the size of the original vector.
    CopyMemory(&ListOfData[0], Data, sizeof(Model) * LD[2]);

    return &ListOfData[0];
}

First of all, you should not use template specialization of function templates. Here is an explanation why. You must use overloading instead. So, your "Model" Serialize function should just be a straight overload:

void Serialize(unsigned char* &Destination, const Model& M) {
    Serialize(Destination, M.SX);
    Serialize(Destination, M.SY);
    Serialize(Destination, M.Stride);
    Serialize(Destination, M.ID);
    Serialize(Destination, M.TriangleCount);
    Serialize(Destination, M.Vertices);
};

Second, when doing serialization, you will need more complexity than that because you'll have trouble with more complex cases. Usually, one would create a "serializer" class (and possibly a separate input and output serializer class) that acts pretty much like iostreams in standard C++ libraries. In the same way as you can make you classes (or structs) outputtable and inputtable to and from a standard iostream object (by overloading the << and >> operators), you do the same for your serialization code.

The general idea when doing serialization is that the mechanism for deserializing should be the exact opposite of the process of serializing. It is also a good idea, generally, to allow for more flexibility in the destination of the serialization such that you can use the same serialize/deserialize code for many purposes, such as saving/loading in files (of different formats), sending/receiving objects across a network, or sharing data between DLLs (modules). I suggest you take a look at some of the existing serialization libraries and get inspiration from that. Two examples I can think of are Boost.Serialization and my own serialization library (part of a bigger library), they both work in a very similar fashion:

  • Base-classes for input-serializers and output-serializers.
  • Standard function (or mechanism) to serialize/deserialize an object (primitive or custom class) which can be overloaded for any special types.
  • A base-class to inherit from if you make custom classes that you want to be serializable.
  • A means to identity types dynamic (e.g., like a RTTI system) and a means to construct dynamically-typed objects of any type (derived from the serializable base-class).
  • A mechanism for flattening out object hierarchies such that multiple cross-referenced objects don't get serialized more than once in each serialized stream of data.
  • A set of input-/output-serializers (derived from the serializer base-classes) for different types of underlying output (xml or binary files, network packets, standard serialization formats like JSON or "Google Protocol Buffers" (or protobuf), or a binary memory-mapped file).

Hmm but that's the problem. I cannot use boost/libraries. I would since I have it installed but I can't.

That's why I just wrote the essentials for Copying. I'm just having a really hard time reading it back. Especially for the vector.

I was going to use osstream and isstream with rdbuff but then it does it via text which would be very large in size considering I might have a vector of structures with a large amount of info.

I thought CopyMemory would be better. I was reading: http://ideone.com/boBt3 and http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/

and a lot of guides but they all seem to be for Java and languages that support this kind of thing natively or for Boost.
I was also thinking of using variadic templates to pass it data that it should serialize (any amount of data members of a struct).

You can make a very simple serializer like this: (warning: this is neither robust nor usable as is, at least, not to my standards)

#include <fstream>

class iserializer {
  private:
    std::ifstream in_file;
  public:

    iserializer(const std::string& aFileName) : in_file(aFileName.c_str(), std::ios_base::binary) { };

    template <typename T>
    friend
    iserializer& operator >>(iserializer& in_serial, T& value) {
      in_serial.in_file.read((char*)&value, sizeof(T));
      return in_serial;
    };
};

template <typename T, typename Allocator>
iserializer& operator >>(iserializer& in_serial, std::vector<T,Allocator>& value) {
  typedef typename std::vector<T,Allocator>::size_type SizeType;
  SizeType sz;
  in_serial >> sz;
  value.reserve(sz);
  for(SizeType i = 0; i < sz; ++i) {
    T tmp = T();
    in_serial >> tmp;
    value.push_back(tmp);
  };
  return in_serial;
};

template <typename T, typename Allocator>
iserializer& operator >>(iserializer& in_serial, std::list<T,Allocator>& value) {
  typedef typename std::list<T,Allocator>::size_type SizeType;
  SizeType sz;
  in_serial >> sz;
  for(SizeType i = 0; i < sz; ++i) {
    T tmp = T();
    in_serial >> tmp;
    value.push_back(tmp);
  };
  return in_serial;
};

class oserializer {
  private:
    std::ofstream out_file;
  public:

    oserializer(const std::string& aFileName) : out_file(aFileName.c_str(), std::ios_base::binary) { };

    template <typename T>
    friend
    oserializer& operator <<(oserializer& out_serial, const T& value) {
      out_serial.out_file.write((const char*)&value, sizeof(T));
      return out_serial;
    };
};

template <typename T, typename Allocator>
oserializer& operator <<(oserializer& out_serial, const std::vector<T,Allocator>& value) {
  typedef typename std::vector<T,Allocator>::const_iterator Iter;
  out_serial << value.size();
  for(Iter it = value.begin(); it != value.end(); ++it)
    out_serial << (*it);
  return out_serial;
};

template <typename T, typename Allocator>
oserializer& operator <<(oserializer& out_serial, const std::list<T,Allocator>& value) {
  typedef typename std::list<T,Allocator>::const_iterator Iter;
  out_serial << value.size();
  for(Iter it = value.begin(); it != value.end(); ++it)
    out_serial << (*it);
  return out_serial;
};

Then, your custom classes or struct simply need to overload the << and >> operator, just like you would for iostreams:

struct Model
{
    GLint SX, SY;
    GLuint Stride;
    unsigned long ID;
    GLint TriangleCount;
    GLboolean NullVertex;
    GLboolean ShowVertices;
    //const GLvoid* VertexPointer; // can't deal with that, should be a vector instead.
    std::vector<Vector3D> Vertices;
};

iserializer& operator >>(iserializer& in_serial, Model& mdl) {
  return in_serial >> mdl.SX >> mdl.SY 
                   >> mdl.Stride 
                   >> mdl.ID 
                   >> mdl.TriangleCount 
                   >> mdl.Vertices;
};

oserializer& operator <<(oserializer& out_serial, const Model& mdl) {
  return out_serial << mdl.SX << mdl.SY 
                    << mdl.Stride 
                    << mdl.ID 
                    << mdl.TriangleCount 
                    << mdl.Vertices; 
};

If you don't want to use file-stream objects, you can certainly use whatever else allows for binary read/write. You could also make the iserializer/oserializer classes into base-classes for any type of output (file, memory, etc.), but that can get a bit more complicated (because virtual functions cannot be templated), which is basically what I did with my iarchive/oarchive classes in my library (you can see all the protected virtual functions for each type of primitive, which all have to be implemented in derived classes).

commented: I solved it earlier but this is good! I'll try this on memory. =] +6

Hey I've edited your code if you don't mind me using it. It works quite well. I was reading: http://www.mr-edd.co.uk/blog/beginners_guide_streambuf for the last couple hours to learn how to use my own streams.

and I changed yours to:

std::istringstream in_file;

public:

iserealizer(const void *Buffer, size_t Size)
{
int_file.rdbuf()->pubsetbuf((char*)Buffer, Size);
};

plus I derived from streambuf and stuff and added onto the class as you suggested. This is pretty fun :) Seems to work pretty well for what I need. Thank you.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.