Fast and Easy C++ Object Serializer

Friday Sep 23rd 2016
Share:

This is a set of templates and macros to provide the ability to easily serialize any object to a string stream.

By Badger McFly

Using the Cheerio Library

In your class definition, create a serialization map as follows:

#include <Cheerio/AllSerializers.h>
class MyObject
{
   public:
      BEGIN_CHEERIO_MAP(MyObject)
         CHEERIO_ENTRY(m_nInt)
         CHEERIO_ENTRY(m_strWide)
         CHEERIO_ENTRY(m_map)
   END_CHEERIO_MAP();
   // You can also use CHEERIO_PARENT() to serialize base classes.

private:
   int m_nInt;
   std::basic_string<char> m_strWide;
   std::map<int, std::string> m_map;
};

In your header file, provide the functions to serialize/de-serialize the custom object:

CHEERIO_OBJECT_SERIALIZER(MyObject);

Then you're done :)

To serialize/de-serialize:

void TestSerialize()
{
   using namespace Cheerio::Cereal;

   std::string strBuff;
   {
      MyObject obj;
      Serialize(obj, strBuff);
   }
   {
      MyObject obj;
      DeSerialize(obj, strBuff);
   }
}

If you want to serialize many objects/native types, you can use the overloaded operators (<< and >>), as such:

void TestMany()
{
   using namespace std;
   using namespace Cheerio;
   using namespace Cereal;

   typedef vector<string> VectorStrings;
   typedef list<wstring>  ListWideStrings;
   string strBuff;

   // Serialize lots of stuff using operator overloads.
   // Catch exceptions thrown if the dynamic allocation fails.
   try
   {
      MyObject         obj;
      int              n666;
      string           is;
      ListWideStrings  theNumberOf;
      VectorStrings    theBeast;

      n666 = 666;
      is = " is ";
      theNumberOf.push_back(L" the ");
      theNumberOf.push_back(L" number ");
      theNumberOf.push_back(L" of ");
      theBeast.push_back(" the ");
      theBeast.push_back(" beast");
      HeapTargetBuffer buff(strBuff);
      buff << n666 << is << theNumberOf << theBeast;
      buff << obj;
   }
   catch (bad_alloc & e)   // std::string.resize()
   {
      cout << "Exception! " << e.what();
   }
   catch (Cereal::Exception & e)
   {
      cout << "Exception! " << e.what();
   }

   // Deserialize objects in the same order.
   // Ensure we catch cheeky exceptions for when the buffer
   // is too small for the object(s).
   try
   {
      MyObject         obj;
      int              n666;
      string           is;
      ListWideStrings  theNumberOf;
      VectorStrings    theBeast;

      SourceBuffer buff(strBuff);
      buff >> n666 >> is >> theNumberOf >> theBeast;
      buff >> obj;
   }
   catch (Cereal::Exception & e)
   {
      cout << "Exception! " << e.what();
   }
}

The buffers can be heap allocated or static. Versioning is also supported and the templates are extendable. See the source code for more complex examples.

This has been tested on VS2010 and VS2015 for 32-bit and 64-bit, but should work on any decent compiler that understands templates.

I originally wrote this to pass objects from Windows to Android over the network. As such, it converts from host types to network types when serializing and back again when de-serializing. This does mean that, for Windows, you will need to link to w32_2.lib, which provides the htons type functions.

If you are interested in how it works, there is a decent amount of comments in the source code, but I'd be happy to answer any questions or review suggestions for improvement.

One example I thought of is providing a plug-in means of adding any new serialization code, such as for XML. This just uses a binary format.

Enjoy.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved