<TRL>

Tutorial

To create a serializeable class you need to use the following macro's to define the members.

class SimpleClass
{
   TRL_BEGIN_MEMBERS(SimpleClass)
   TRL_MEMBER(private, int, m_count)
   TRL_MEMBER(private, std::string, m_name)
   TRL_END_MEMBERS()
};

The TRL_BEGIN_MEMBERS macro defines a default constructor so you can't define it yourself. You can put these macro's anywhere in the class and not every line between TRL_BEGIN_MEMBERS and TRL_END_MEMBERS needs to contain a call to TRL_MEMBER. So a TRL_MEMBER call can take up multiple lines. You can use all the basic types and std::string and std::wstring as members. You can also use smart pointers and standard containers, these are explained later on. First lets look at the serialize and deserialize functions.

int main(void)
{
   SimpleClass obj;
   long length = 0;
   char* data = trl::serialize(obj, length);
   SimpleClass* ptr = trl::deserialize<SimpleClass>(data, length);
   delete ptr;
   delete[] data;
}

As you can see the serialize function takes a const reference to the object and a reference to a long as input parameters. This long is filled in to give the length of the serialized data. The deserialize class returns a pointer to an on heap allocated object. It's up to you to do the memory management of this object. If the amount of data that is consumed by deserialize doesn't match the length that is passed in, an exception is thrown.

You can also use composition.

class CompositeClass
{
   TRL_BEGIN_MEMBERS(CompositeClass)
   TRL_MEMBER(private, SimpleClass, m_simple)
   TRL_MEMBER(private, int, m_value)
   TRL_END_MEMBERS()
};

As smart pointers you can use std::auto_ptr, boost::shared_ptr and boost::weak_ptr. If you are using the c++0x version these become std::unique_ptr, std::shared_ptr and std::weak_ptr.

class PointerClass
{
   TRL_BEGIN_MEMBERS(PointerClass)
   TRL_MEMBER(private, std::auto_ptr<CompositeClass>, m_auto)
   TRL_MEMBER(private, boost::shared_ptr<SimpleClass>, m_shared)
   TRL_MEMBER(private, boost::weak_ptr<SimpleClass>, m_weak)
   TRL_END_MEMBERS()
};

All standard containers are supported. If you are using the c++0x version, the newly added containers are also supported.

class ContainerClass
{
   TRL_BEGIN_MEMBERS(ContainerClass)
   TRL_MEMBER(private, std::vector<SimpleClass>, m_vector)
   TRL_MEMBER(private, std::list<CompositeClass>, m_list)

   typedef std::map<std::string, SimpleClass> SimpleMap;

   TRL_MEMBER(private, SimpleMap, m_map)
   TRL_END_MEMBERS()
};

As you can see the example for the map is a bit more involving, you need to define a typedef first. This is always the case if you want to use types that have multiple template parameters. You can also combine smart pointers and containers, so you can have a vector of shared_ptr.

Furthermore std::pair, std::complex and std::valarray are also supported.

If you want to use inheritance you need to add an extra macro to define the base class.

class DerivedClass : public CompositeClass
{
   TRL_BEGIN_MEMBERS(DerivedClass)
   TRL_BASE_CLASS(CompositeClass)
   TRL_MEMBER(private, std::string, m_text)
   TRL_END_MEMBERS()
};

If you want to use a derived class in a polymorphic way, eg. store pointers to the base class, you need an extra macro. This one has to be put in the *.cpp file. You need to call this macro for the derived class and all the base classes that can be constructed by users of the class.

TRL_POLYMORPH(CompositeClass)

TRL_POLYMORPH(DerivedClass)

You can also use multiple inheritance. For this you simply need to add an extra TRL_BASE_CLASS macro.

class MultipleClass : public CompositeClass, public PointerClass
{
   TRL_BEGIN_MEMBERS(MultipleClass)
   TRL_BASE_CLASS(CompositeClass)
   TRL_BASE_CLASS(PointerClass)
   TRL_MEMBER(private, std::string, m_text)
   TRL_END_MEMBERS()
};

If you want to use multiple inheritance in a polymorphic way, it gets a bit more complicated. You still need to call TRL_POLYMORPH for all the base classes (that can be constructed by the users), but for the derived class you need to call a special macro TRL_POLYMORPH_MULTIPLE_X where X is the number of direct and indirect base classes. The first argument of this macro is the derived class, the following arguments are all the base classes in any order.

TRL_POLYMORPH(CompositeClass)

TRL_POLYMORPH(PointerClass)

TRL_POLYMORPH_MULTIPLE_2(MultipleClass, CompositeClass, PointerClass)

If you need to do some initialization after an object is deserialized, you can do this by adding a static initialize member function to the class.

class InitializeClass
{
public:
   static void initialize(InitializeClass& obj, int version)
   {
      obj.m_sum = obj.m_val0 + obj.m_val1;
   }

   TRL_BEGIN_MEMBERS(InitializeClass)
   TRL_MEMBER(private, int, m_val0)
   TRL_MEMBER(private, int, m_val1)
   TRL_END_MEMBERS()

private:
   int m_sum;
};

This initialize function is called after the object is completely constructed. It is called for all the base classes in the correct order.

You can also set the memory alignment and endian type when you call a serialize or deserialize function. Using the platform memory alignment (the default) gives the best performance but results in larger files. For the smallest file use an alignment of 1. For endian support you can use the macro's TRL_LITTLE_ENDIAN and TRL_BIG_ENDIAN.

int main(void)
{
   SimpleClass obj;
   long length = 0;
   char* data = trl::serialize<4, TRL_LITTLE_ENDIAN>(obj, length);
   SimpleClass* ptr = trl::deserialize<SimpleClass, 4, TRL_LITTLE_ENDIAN>(data, length);
   delete ptr;
   delete[] data;
}

You can also use versioning with TRL. For this you use the macro TRL_MEMBER_V.

class SimpleClass
{
   TRL_BEGIN_MEMBERS(SimpleClass)
   TRL_MEMBER(private, int, m_count)
   TRL_MEMBER(private, std::string, m_name)
   TRL_MEMBER_V(private, float, m_factor, 2)
   TRL_END_MEMBERS()
};

int main(void)
{
   SimpleClass obj;
   long length = 0;
   char* data = trl::serialize(obj, length, 1);
   SimpleClass* ptr = trl::deserialize<SimpleClass>(data, length, 1);
   delete ptr;
   delete[] data;
}

In this example the float doesn't get serialized or deserialized because it has version 2. You can still initialize it afterwards with an initialize function.